home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / feedparser.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  89KB  |  3,093 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. __version__ = '4.1'
  5. __license__ = "Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice,\n  this list of conditions and the following disclaimer.\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE."
  6. __author__ = 'Mark Pilgrim <http://diveintomark.org/>'
  7. __contributors__ = [
  8.     'Jason Diamond <http://injektilo.org/>',
  9.     'John Beimler <http://john.beimler.org/>',
  10.     'Fazal Majid <http://www.majid.info/mylos/weblog/>',
  11.     'Aaron Swartz <http://aaronsw.com/>',
  12.     'Kevin Marks <http://epeus.blogspot.com/>']
  13. _debug = 0
  14. USER_AGENT = 'UniversalFeedParser/%s +http://feedparser.org/' % __version__
  15. ACCEPT_HEADER = 'application/atom+xml,application/rdf+xml,application/rss+xml,application/x-netcdf,application/xml;q=0.9,text/xml;q=0.2,*/*;q=0.1'
  16. PREFERRED_XML_PARSERS = [
  17.     'drv_libxml2']
  18. TIDY_MARKUP = 0
  19. PREFERRED_TIDY_INTERFACES = [
  20.     'uTidy',
  21.     'mxTidy']
  22. import sgmllib
  23. import re
  24. import sys
  25. import copy
  26. import urlparse
  27. import time
  28. import rfc822
  29. import types
  30. import cgi
  31. import urllib
  32. import urllib2
  33.  
  34. try:
  35.     from cStringIO import StringIO as _StringIO
  36. except:
  37.     from StringIO import StringIO as _StringIO
  38.  
  39.  
  40. try:
  41.     import gzip
  42. except:
  43.     gzip = None
  44.  
  45.  
  46. try:
  47.     import zlib
  48. except:
  49.     zlib = None
  50.  
  51.  
  52. try:
  53.     import xml.sax as xml
  54.     xml.sax.make_parser(PREFERRED_XML_PARSERS)
  55.     from xml.sax.saxutils import escape as _xmlescape
  56.     _XML_AVAILABLE = 1
  57. except:
  58.     _XML_AVAILABLE = 0
  59.     
  60.     def _xmlescape(data):
  61.         data = data.replace('&', '&')
  62.         data = data.replace('>', '>')
  63.         data = data.replace('<', '<')
  64.         return data
  65.  
  66.  
  67.  
  68. try:
  69.     import base64
  70.     import binascii
  71. except:
  72.     base64 = binascii = None
  73.  
  74.  
  75. try:
  76.     import cjkcodecs.aliases as cjkcodecs
  77. except:
  78.     pass
  79.  
  80.  
  81. try:
  82.     import iconv_codec
  83. except:
  84.     pass
  85.  
  86.  
  87. try:
  88.     import chardet
  89.     if _debug:
  90.         import chardet.constants as chardet
  91.         chardet.constants._debug = 1
  92. except:
  93.     chardet = None
  94.  
  95.  
  96. class ThingsNobodyCaresAboutButMe(Exception):
  97.     pass
  98.  
  99.  
  100. class CharacterEncodingOverride(ThingsNobodyCaresAboutButMe):
  101.     pass
  102.  
  103.  
  104. class CharacterEncodingUnknown(ThingsNobodyCaresAboutButMe):
  105.     pass
  106.  
  107.  
  108. class NonXMLContentType(ThingsNobodyCaresAboutButMe):
  109.     pass
  110.  
  111.  
  112. class UndeclaredNamespace(Exception):
  113.     pass
  114.  
  115. sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
  116. sgmllib.special = re.compile('<!')
  117. sgmllib.charref = re.compile('&#(x?[0-9A-Fa-f]+)[^0-9A-Fa-f]')
  118. SUPPORTED_VERSIONS = {
  119.     '': 'unknown',
  120.     'rss090': 'RSS 0.90',
  121.     'rss091n': 'RSS 0.91 (Netscape)',
  122.     'rss091u': 'RSS 0.91 (Userland)',
  123.     'rss092': 'RSS 0.92',
  124.     'rss093': 'RSS 0.93',
  125.     'rss094': 'RSS 0.94',
  126.     'rss20': 'RSS 2.0',
  127.     'rss10': 'RSS 1.0',
  128.     'rss': 'RSS (unknown version)',
  129.     'atom01': 'Atom 0.1',
  130.     'atom02': 'Atom 0.2',
  131.     'atom03': 'Atom 0.3',
  132.     'atom10': 'Atom 1.0',
  133.     'atom': 'Atom (unknown version)',
  134.     'cdf': 'CDF',
  135.     'hotrss': 'Hot RSS' }
  136.  
  137. try:
  138.     UserDict = dict
  139. except NameError:
  140.     from UserDict import UserDict
  141.     
  142.     def dict(aList):
  143.         rc = { }
  144.         for k, v in aList:
  145.             rc[k] = v
  146.         
  147.         return rc
  148.  
  149.  
  150.  
  151. class FeedParserDict(UserDict):
  152.     keymap = {
  153.         'channel': 'feed',
  154.         'items': 'entries',
  155.         'guid': 'id',
  156.         'date': 'updated',
  157.         'date_parsed': 'updated_parsed',
  158.         'description': [
  159.             'subtitle',
  160.             'summary'],
  161.         'url': [
  162.             'href'],
  163.         'modified': 'updated',
  164.         'modified_parsed': 'updated_parsed',
  165.         'issued': 'published',
  166.         'issued_parsed': 'published_parsed',
  167.         'copyright': 'rights',
  168.         'copyright_detail': 'rights_detail',
  169.         'tagline': 'subtitle',
  170.         'tagline_detail': 'subtitle_detail' }
  171.     
  172.     def __getitem__(self, key):
  173.         if key == 'category':
  174.             return UserDict.__getitem__(self, 'tags')[0]['term']
  175.         
  176.         realkey = self.keymap.get(key, key)
  177.         if type(realkey) == types.ListType:
  178.             for k in realkey:
  179.                 if UserDict.has_key(self, k):
  180.                     return UserDict.__getitem__(self, k)
  181.                     continue
  182.                 None if key == 'categories' else []
  183.             
  184.         
  185.         if UserDict.has_key(self, key):
  186.             return UserDict.__getitem__(self, key)
  187.         
  188.         return UserDict.__getitem__(self, realkey)
  189.  
  190.     
  191.     def __setitem__(self, key, value):
  192.         for k in self.keymap.keys():
  193.             if key == k:
  194.                 key = self.keymap[k]
  195.                 if type(key) == types.ListType:
  196.                     key = key[0]
  197.                 
  198.             type(key) == types.ListType
  199.         
  200.         return UserDict.__setitem__(self, key, value)
  201.  
  202.     
  203.     def get(self, key, default = None):
  204.         if self.has_key(key):
  205.             return self[key]
  206.         else:
  207.             return default
  208.  
  209.     
  210.     def setdefault(self, key, value):
  211.         if not self.has_key(key):
  212.             self[key] = value
  213.         
  214.         return self[key]
  215.  
  216.     
  217.     def has_key(self, key):
  218.         
  219.         try:
  220.             if not hasattr(self, key):
  221.                 pass
  222.             return UserDict.has_key(self, key)
  223.         except AttributeError:
  224.             return False
  225.  
  226.  
  227.     
  228.     def __getattr__(self, key):
  229.         
  230.         try:
  231.             return self.__dict__[key]
  232.         except KeyError:
  233.             pass
  234.  
  235.         
  236.         try:
  237.             return self.__getitem__(key)
  238.         except:
  239.             raise AttributeError, "object has no attribute '%s'" % key
  240.  
  241.  
  242.     
  243.     def __setattr__(self, key, value):
  244.         if key.startswith('_') or key == 'data':
  245.             self.__dict__[key] = value
  246.         else:
  247.             return self.__setitem__(key, value)
  248.  
  249.     
  250.     def __contains__(self, key):
  251.         return self.has_key(key)
  252.  
  253.  
  254.  
  255. def zopeCompatibilityHack():
  256.     global FeedParserDict
  257.     del FeedParserDict
  258.     
  259.     def FeedParserDict(aDict = None):
  260.         rc = { }
  261.         if aDict:
  262.             rc.update(aDict)
  263.         
  264.         return rc
  265.  
  266.  
  267. _ebcdic_to_ascii_map = None
  268.  
  269. def _ebcdic_to_ascii(s):
  270.     global _ebcdic_to_ascii_map
  271.     if not _ebcdic_to_ascii_map:
  272.         emap = (0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255)
  273.         import string as string
  274.         _ebcdic_to_ascii_map = string.maketrans(''.join(map(chr, range(256))), ''.join(map(chr, emap)))
  275.     
  276.     return s.translate(_ebcdic_to_ascii_map)
  277.  
  278. _urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)')
  279.  
  280. def _urljoin(base, uri):
  281.     uri = _urifixer.sub('\\1\\3', uri)
  282.     return urlparse.urljoin(base, uri)
  283.  
  284.  
  285. class _FeedParserMixin:
  286.     namespaces = {
  287.         '': '',
  288.         'http://backend.userland.com/rss': '',
  289.         'http://blogs.law.harvard.edu/tech/rss': '',
  290.         'http://purl.org/rss/1.0/': '',
  291.         'http://my.netscape.com/rdf/simple/0.9/': '',
  292.         'http://example.com/newformat#': '',
  293.         'http://example.com/necho': '',
  294.         'http://purl.org/echo/': '',
  295.         'uri/of/echo/namespace#': '',
  296.         'http://purl.org/pie/': '',
  297.         'http://purl.org/atom/ns#': '',
  298.         'http://www.w3.org/2005/Atom': '',
  299.         'http://purl.org/rss/1.0/modules/rss091#': '',
  300.         'http://webns.net/mvcb/': 'admin',
  301.         'http://purl.org/rss/1.0/modules/aggregation/': 'ag',
  302.         'http://purl.org/rss/1.0/modules/annotate/': 'annotate',
  303.         'http://media.tangent.org/rss/1.0/': 'audio',
  304.         'http://backend.userland.com/blogChannelModule': 'blogChannel',
  305.         'http://web.resource.org/cc/': 'cc',
  306.         'http://backend.userland.com/creativeCommonsRssModule': 'creativeCommons',
  307.         'http://purl.org/rss/1.0/modules/company': 'co',
  308.         'http://purl.org/rss/1.0/modules/content/': 'content',
  309.         'http://my.theinfo.org/changed/1.0/rss/': 'cp',
  310.         'http://purl.org/dc/elements/1.1/': 'dc',
  311.         'http://purl.org/dc/terms/': 'dcterms',
  312.         'http://purl.org/rss/1.0/modules/email/': 'email',
  313.         'http://purl.org/rss/1.0/modules/event/': 'ev',
  314.         'http://rssnamespace.org/feedburner/ext/1.0': 'feedburner',
  315.         'http://freshmeat.net/rss/fm/': 'fm',
  316.         'http://xmlns.com/foaf/0.1/': 'foaf',
  317.         'http://www.w3.org/2003/01/geo/wgs84_pos#': 'geo',
  318.         'http://postneo.com/icbm/': 'icbm',
  319.         'http://purl.org/rss/1.0/modules/image/': 'image',
  320.         'http://www.itunes.com/DTDs/PodCast-1.0.dtd': 'itunes',
  321.         'http://example.com/DTDs/PodCast-1.0.dtd': 'itunes',
  322.         'http://purl.org/rss/1.0/modules/link/': 'l',
  323.         'http://search.yahoo.com/mrss': 'media',
  324.         'http://madskills.com/public/xml/rss/module/pingback/': 'pingback',
  325.         'http://prismstandard.org/namespaces/1.2/basic/': 'prism',
  326.         'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf',
  327.         'http://www.w3.org/2000/01/rdf-schema#': 'rdfs',
  328.         'http://purl.org/rss/1.0/modules/reference/': 'ref',
  329.         'http://purl.org/rss/1.0/modules/richequiv/': 'reqv',
  330.         'http://purl.org/rss/1.0/modules/search/': 'search',
  331.         'http://purl.org/rss/1.0/modules/slash/': 'slash',
  332.         'http://schemas.xmlsoap.org/soap/envelope/': 'soap',
  333.         'http://purl.org/rss/1.0/modules/servicestatus/': 'ss',
  334.         'http://hacks.benhammersley.com/rss/streaming/': 'str',
  335.         'http://purl.org/rss/1.0/modules/subscription/': 'sub',
  336.         'http://purl.org/rss/1.0/modules/syndication/': 'sy',
  337.         'http://purl.org/rss/1.0/modules/taxonomy/': 'taxo',
  338.         'http://purl.org/rss/1.0/modules/threading/': 'thr',
  339.         'http://purl.org/rss/1.0/modules/textinput/': 'ti',
  340.         'http://madskills.com/public/xml/rss/module/trackback/': 'trackback',
  341.         'http://wellformedweb.org/commentAPI/': 'wfw',
  342.         'http://purl.org/rss/1.0/modules/wiki/': 'wiki',
  343.         'http://www.w3.org/1999/xhtml': 'xhtml',
  344.         'http://www.w3.org/XML/1998/namespace': 'xml',
  345.         'http://schemas.pocketsoap.com/rss/myDescModule/': 'szf' }
  346.     _matchnamespaces = { }
  347.     can_be_relative_uri = [
  348.         'link',
  349.         'id',
  350.         'wfw_comment',
  351.         'wfw_commentrss',
  352.         'docs',
  353.         'url',
  354.         'href',
  355.         'comments',
  356.         'license',
  357.         'icon',
  358.         'logo']
  359.     can_contain_relative_uris = [
  360.         'content',
  361.         'title',
  362.         'summary',
  363.         'info',
  364.         'tagline',
  365.         'subtitle',
  366.         'copyright',
  367.         'rights',
  368.         'description']
  369.     can_contain_dangerous_markup = [
  370.         'content',
  371.         'title',
  372.         'summary',
  373.         'info',
  374.         'tagline',
  375.         'subtitle',
  376.         'copyright',
  377.         'rights',
  378.         'description']
  379.     html_types = [
  380.         'text/html',
  381.         'application/xhtml+xml']
  382.     
  383.     def __init__(self, baseuri = None, baselang = None, encoding = 'utf-8'):
  384.         if _debug:
  385.             sys.stderr.write('initializing FeedParser\n')
  386.         
  387.         if not self._matchnamespaces:
  388.             for k, v in self.namespaces.items():
  389.                 self._matchnamespaces[k.lower()] = v
  390.             
  391.         
  392.         self.feeddata = FeedParserDict()
  393.         self.encoding = encoding
  394.         self.entries = []
  395.         self.version = ''
  396.         self.namespacesInUse = { }
  397.         self.infeed = 0
  398.         self.inentry = 0
  399.         self.incontent = 0
  400.         self.intextinput = 0
  401.         self.inimage = 0
  402.         self.inauthor = 0
  403.         self.incontributor = 0
  404.         self.inpublisher = 0
  405.         self.insource = 0
  406.         self.sourcedata = FeedParserDict()
  407.         self.contentparams = FeedParserDict()
  408.         self._summaryKey = None
  409.         self.namespacemap = { }
  410.         self.elementstack = []
  411.         self.basestack = []
  412.         self.langstack = []
  413.         if not baseuri:
  414.             pass
  415.         self.baseuri = ''
  416.         if not baselang:
  417.             pass
  418.         self.lang = None
  419.         if baselang:
  420.             self.feeddata['language'] = baselang
  421.         
  422.  
  423.     
  424.     def unknown_starttag(self, tag, attrs):
  425.         if _debug:
  426.             sys.stderr.write('start %s with %s\n' % (tag, attrs))
  427.         
  428.         attrs = [ (k.lower(), v) for k, v in attrs ]
  429.         attrs = [ (k, v) for k, v in attrs ]
  430.         attrsD = dict(attrs)
  431.         if not attrsD.get('xml:base', attrsD.get('base')):
  432.             pass
  433.         baseuri = self.baseuri
  434.         self.baseuri = _urljoin(self.baseuri, baseuri)
  435.         lang = attrsD.get('xml:lang', attrsD.get('lang'))
  436.         if lang == '':
  437.             lang = None
  438.         elif lang is None:
  439.             lang = self.lang
  440.         
  441.         self.lang = lang
  442.         self.basestack.append(self.baseuri)
  443.         self.langstack.append(lang)
  444.         for prefix, uri in attrs:
  445.             if prefix.startswith('xmlns:'):
  446.                 self.trackNamespace(prefix[6:], uri)
  447.                 continue
  448.             None if lang else []
  449.             if prefix == 'xmlns':
  450.                 self.trackNamespace(None, uri)
  451.                 continue
  452.         
  453.         if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
  454.             self.contentparams['type'] = 'application/xhtml+xml'
  455.         
  456.         if tag.find(':') != -1:
  457.             (prefix, suffix) = tag.split(':', 1)
  458.         else:
  459.             prefix = ''
  460.             suffix = tag
  461.         prefix = self.namespacemap.get(prefix, prefix)
  462.         if prefix:
  463.             prefix = prefix + '_'
  464.         
  465.         if not prefix and tag not in ('title', 'link', 'description', 'name'):
  466.             self.intextinput = 0
  467.         
  468.         if not prefix and tag not in ('title', 'link', 'description', 'url', 'href', 'width', 'height'):
  469.             self.inimage = 0
  470.         
  471.         methodname = '_start_' + prefix + suffix
  472.         
  473.         try:
  474.             method = getattr(self, methodname)
  475.             return method(attrsD)
  476.         except AttributeError:
  477.             return self.push(prefix + suffix, 1)
  478.  
  479.  
  480.     
  481.     def unknown_endtag(self, tag):
  482.         if _debug:
  483.             sys.stderr.write('end %s\n' % tag)
  484.         
  485.         if tag.find(':') != -1:
  486.             (prefix, suffix) = tag.split(':', 1)
  487.         else:
  488.             prefix = ''
  489.             suffix = tag
  490.         prefix = self.namespacemap.get(prefix, prefix)
  491.         if prefix:
  492.             prefix = prefix + '_'
  493.         
  494.         methodname = '_end_' + prefix + suffix
  495.         
  496.         try:
  497.             method = getattr(self, methodname)
  498.             method()
  499.         except AttributeError:
  500.             self.pop(prefix + suffix)
  501.  
  502.         if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
  503.             self.contentparams['type'] = 'application/xhtml+xml'
  504.         
  505.         if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
  506.             tag = tag.split(':')[-1]
  507.             self.handle_data('</%s>' % tag, escape = 0)
  508.         
  509.         if self.basestack:
  510.             self.basestack.pop()
  511.             if self.basestack and self.basestack[-1]:
  512.                 self.baseuri = self.basestack[-1]
  513.             
  514.         
  515.         if self.langstack:
  516.             self.langstack.pop()
  517.             if self.langstack:
  518.                 self.lang = self.langstack[-1]
  519.             
  520.         
  521.  
  522.     
  523.     def handle_charref(self, ref):
  524.         if not self.elementstack:
  525.             return None
  526.         
  527.         ref = ref.lower()
  528.         if ref in ('34', '38', '39', '60', '62', 'x22', 'x26', 'x27', 'x3c', 'x3e'):
  529.             text = '&#%s;' % ref
  530.         elif ref[0] == 'x':
  531.             c = int(ref[1:], 16)
  532.         else:
  533.             c = int(ref)
  534.         text = unichr(c).encode('utf-8')
  535.         self.elementstack[-1][2].append(text)
  536.  
  537.     
  538.     def handle_entityref(self, ref):
  539.         if not self.elementstack:
  540.             return None
  541.         
  542.         if _debug:
  543.             sys.stderr.write('entering handle_entityref with %s\n' % ref)
  544.         
  545.         if ref in ('lt', 'gt', 'quot', 'amp', 'apos'):
  546.             text = '&%s;' % ref
  547.         else:
  548.             
  549.             def name2cp(k):
  550.                 import htmlentitydefs as htmlentitydefs
  551.                 if hasattr(htmlentitydefs, 'name2codepoint'):
  552.                     return htmlentitydefs.name2codepoint[k]
  553.                 
  554.                 k = htmlentitydefs.entitydefs[k]
  555.                 if k.startswith('&#') and k.endswith(';'):
  556.                     return int(k[2:-1])
  557.                 
  558.                 return ord(k)
  559.  
  560.             
  561.             try:
  562.                 name2cp(ref)
  563.             except KeyError:
  564.                 text = '&%s;' % ref
  565.  
  566.             text = unichr(name2cp(ref)).encode('utf-8')
  567.         self.elementstack[-1][2].append(text)
  568.  
  569.     
  570.     def handle_data(self, text, escape = 1):
  571.         if not self.elementstack:
  572.             return None
  573.         
  574.         if escape and self.contentparams.get('type') == 'application/xhtml+xml':
  575.             text = _xmlescape(text)
  576.         
  577.         self.elementstack[-1][2].append(text)
  578.  
  579.     
  580.     def handle_comment(self, text):
  581.         pass
  582.  
  583.     
  584.     def handle_pi(self, text):
  585.         pass
  586.  
  587.     
  588.     def handle_decl(self, text):
  589.         pass
  590.  
  591.     
  592.     def parse_declaration(self, i):
  593.         if _debug:
  594.             sys.stderr.write('entering parse_declaration\n')
  595.         
  596.         if self.rawdata[i:i + 9] == '<![CDATA[':
  597.             k = self.rawdata.find(']]>', i)
  598.             if k == -1:
  599.                 k = len(self.rawdata)
  600.             
  601.             self.handle_data(_xmlescape(self.rawdata[i + 9:k]), 0)
  602.             return k + 3
  603.         else:
  604.             k = self.rawdata.find('>', i)
  605.             return k + 1
  606.  
  607.     
  608.     def mapContentType(self, contentType):
  609.         contentType = contentType.lower()
  610.         if contentType == 'text':
  611.             contentType = 'text/plain'
  612.         elif contentType == 'html':
  613.             contentType = 'text/html'
  614.         elif contentType == 'xhtml':
  615.             contentType = 'application/xhtml+xml'
  616.         
  617.         return contentType
  618.  
  619.     
  620.     def trackNamespace(self, prefix, uri):
  621.         loweruri = uri.lower()
  622.         if (prefix, loweruri) == (None, 'http://my.netscape.com/rdf/simple/0.9/') and not (self.version):
  623.             self.version = 'rss090'
  624.         
  625.         if loweruri == 'http://purl.org/rss/1.0/' and not (self.version):
  626.             self.version = 'rss10'
  627.         
  628.         if loweruri == 'http://www.w3.org/2005/atom' and not (self.version):
  629.             self.version = 'atom10'
  630.         
  631.         if loweruri.find('backend.userland.com/rss') != -1:
  632.             uri = 'http://backend.userland.com/rss'
  633.             loweruri = uri
  634.         
  635.         uri[self.namespacesInUse if self._matchnamespaces.has_key(loweruri) else ''] = None
  636.  
  637.     
  638.     def resolveURI(self, uri):
  639.         if not self.baseuri:
  640.             pass
  641.         return _urljoin('', uri)
  642.  
  643.     
  644.     def decodeEntities(self, element, data):
  645.         return data
  646.  
  647.     
  648.     def push(self, element, expectingText):
  649.         self.elementstack.append([
  650.             element,
  651.             expectingText,
  652.             []])
  653.  
  654.     
  655.     def pop(self, element, stripWhitespace = 1):
  656.         if not self.elementstack:
  657.             return None
  658.         
  659.         if self.elementstack[-1][0] != element:
  660.             return None
  661.         
  662.         (element, expectingText, pieces) = self.elementstack.pop()
  663.         output = ''.join(pieces)
  664.         if stripWhitespace:
  665.             output = output.strip()
  666.         
  667.         if not expectingText:
  668.             return output
  669.         
  670.         if base64 and self.contentparams.get('base64', 0):
  671.             
  672.             try:
  673.                 output = base64.decodestring(output)
  674.             except binascii.Error:
  675.                 pass
  676.             except binascii.Incomplete:
  677.                 pass
  678.             except:
  679.                 None<EXCEPTION MATCH>binascii.Error
  680.             
  681.  
  682.         None<EXCEPTION MATCH>binascii.Error
  683.         if element in self.can_be_relative_uri and output:
  684.             output = self.resolveURI(output)
  685.         
  686.         if not self.contentparams.get('base64', 0):
  687.             output = self.decodeEntities(element, output)
  688.         
  689.         
  690.         try:
  691.             del self.contentparams['mode']
  692.         except KeyError:
  693.             pass
  694.  
  695.         
  696.         try:
  697.             del self.contentparams['base64']
  698.         except KeyError:
  699.             pass
  700.  
  701.         if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
  702.             if element in self.can_contain_relative_uris:
  703.                 output = _resolveRelativeURIs(output, self.baseuri, self.encoding)
  704.             
  705.         
  706.         if self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types:
  707.             if element in self.can_contain_dangerous_markup:
  708.                 output = _sanitizeHTML(output, self.encoding)
  709.             
  710.         
  711.         if self.encoding and type(output) != type(u''):
  712.             
  713.             try:
  714.                 output = unicode(output, self.encoding)
  715.  
  716.         
  717.         if element == 'category':
  718.             return output
  719.         
  720.         if self.inentry and not (self.insource):
  721.             if element == 'content':
  722.                 self.entries[-1].setdefault(element, [])
  723.                 contentparams = copy.deepcopy(self.contentparams)
  724.                 contentparams['value'] = output
  725.                 self.entries[-1][element].append(contentparams)
  726.             elif element == 'link':
  727.                 self.entries[-1][element] = output
  728.                 if output:
  729.                     self.entries[-1]['links'][-1]['href'] = output
  730.                 
  731.             elif element == 'description':
  732.                 element = 'summary'
  733.             
  734.             self.entries[-1][element] = output
  735.             if self.incontent:
  736.                 contentparams = copy.deepcopy(self.contentparams)
  737.                 contentparams['value'] = output
  738.                 self.entries[-1][element + '_detail'] = contentparams
  739.             
  740.         elif (self.infeed or self.insource) and not (self.intextinput) and not (self.inimage):
  741.             context = self._getContext()
  742.             if element == 'description':
  743.                 element = 'subtitle'
  744.             
  745.             context[element] = output
  746.             if element == 'link':
  747.                 context['links'][-1]['href'] = output
  748.             elif self.incontent:
  749.                 contentparams = copy.deepcopy(self.contentparams)
  750.                 contentparams['value'] = output
  751.                 context[element + '_detail'] = contentparams
  752.             
  753.         
  754.         return output
  755.  
  756.     
  757.     def pushContent(self, tag, attrsD, defaultContentType, expectingText):
  758.         self.incontent += 1
  759.         self.contentparams = FeedParserDict({
  760.             'type': self.mapContentType(attrsD.get('type', defaultContentType)),
  761.             'language': self.lang,
  762.             'base': self.baseuri })
  763.         self.contentparams['base64'] = self._isBase64(attrsD, self.contentparams)
  764.         self.push(tag, expectingText)
  765.  
  766.     
  767.     def popContent(self, tag):
  768.         value = self.pop(tag)
  769.         self.incontent -= 1
  770.         self.contentparams.clear()
  771.         return value
  772.  
  773.     
  774.     def _mapToStandardPrefix(self, name):
  775.         colonpos = name.find(':')
  776.         if colonpos != -1:
  777.             prefix = name[:colonpos]
  778.             suffix = name[colonpos + 1:]
  779.             prefix = self.namespacemap.get(prefix, prefix)
  780.             name = prefix + ':' + suffix
  781.         
  782.         return name
  783.  
  784.     
  785.     def _getAttribute(self, attrsD, name):
  786.         return attrsD.get(self._mapToStandardPrefix(name))
  787.  
  788.     
  789.     def _isBase64(self, attrsD, contentparams):
  790.         if attrsD.get('mode', '') == 'base64':
  791.             return 1
  792.         
  793.         if self.contentparams['type'].startswith('text/'):
  794.             return 0
  795.         
  796.         if self.contentparams['type'].endswith('+xml'):
  797.             return 0
  798.         
  799.         if self.contentparams['type'].endswith('/xml'):
  800.             return 0
  801.         
  802.         return 1
  803.  
  804.     
  805.     def _itsAnHrefDamnIt(self, attrsD):
  806.         href = attrsD.get('url', attrsD.get('uri', attrsD.get('href', None)))
  807.         if href:
  808.             
  809.             try:
  810.                 del attrsD['url']
  811.             except KeyError:
  812.                 pass
  813.  
  814.             
  815.             try:
  816.                 del attrsD['uri']
  817.             except KeyError:
  818.                 pass
  819.  
  820.             attrsD['href'] = href
  821.         
  822.         return attrsD
  823.  
  824.     
  825.     def _save(self, key, value):
  826.         context = self._getContext()
  827.         context.setdefault(key, value)
  828.  
  829.     
  830.     def _start_rss(self, attrsD):
  831.         versionmap = {
  832.             '0.91': 'rss091u',
  833.             '0.92': 'rss092',
  834.             '0.93': 'rss093',
  835.             '0.94': 'rss094' }
  836.         if not self.version:
  837.             attr_version = attrsD.get('version', '')
  838.             version = versionmap.get(attr_version)
  839.             if version:
  840.                 self.version = version
  841.             elif attr_version.startswith('2.'):
  842.                 self.version = 'rss20'
  843.             else:
  844.                 self.version = 'rss'
  845.         
  846.  
  847.     
  848.     def _start_dlhottitles(self, attrsD):
  849.         self.version = 'hotrss'
  850.  
  851.     
  852.     def _start_channel(self, attrsD):
  853.         self.infeed = 1
  854.         self._cdf_common(attrsD)
  855.  
  856.     _start_feedinfo = _start_channel
  857.     
  858.     def _cdf_common(self, attrsD):
  859.         if attrsD.has_key('lastmod'):
  860.             self._start_modified({ })
  861.             self.elementstack[-1][-1] = attrsD['lastmod']
  862.             self._end_modified()
  863.         
  864.         if attrsD.has_key('href'):
  865.             self._start_link({ })
  866.             self.elementstack[-1][-1] = attrsD['href']
  867.             self._end_link()
  868.         
  869.  
  870.     
  871.     def _start_feed(self, attrsD):
  872.         self.infeed = 1
  873.         versionmap = {
  874.             '0.1': 'atom01',
  875.             '0.2': 'atom02',
  876.             '0.3': 'atom03' }
  877.         if not self.version:
  878.             attr_version = attrsD.get('version')
  879.             version = versionmap.get(attr_version)
  880.             if version:
  881.                 self.version = version
  882.             else:
  883.                 self.version = 'atom'
  884.         
  885.  
  886.     
  887.     def _end_channel(self):
  888.         self.infeed = 0
  889.  
  890.     _end_feed = _end_channel
  891.     
  892.     def _start_image(self, attrsD):
  893.         self.inimage = 1
  894.         self.push('image', 0)
  895.         context = self._getContext()
  896.         context.setdefault('image', FeedParserDict())
  897.  
  898.     
  899.     def _end_image(self):
  900.         self.pop('image')
  901.         self.inimage = 0
  902.  
  903.     
  904.     def _start_textinput(self, attrsD):
  905.         self.intextinput = 1
  906.         self.push('textinput', 0)
  907.         context = self._getContext()
  908.         context.setdefault('textinput', FeedParserDict())
  909.  
  910.     _start_textInput = _start_textinput
  911.     
  912.     def _end_textinput(self):
  913.         self.pop('textinput')
  914.         self.intextinput = 0
  915.  
  916.     _end_textInput = _end_textinput
  917.     
  918.     def _start_author(self, attrsD):
  919.         self.inauthor = 1
  920.         self.push('author', 1)
  921.  
  922.     _start_managingeditor = _start_author
  923.     _start_dc_author = _start_author
  924.     _start_dc_creator = _start_author
  925.     _start_itunes_author = _start_author
  926.     
  927.     def _end_author(self):
  928.         self.pop('author')
  929.         self.inauthor = 0
  930.         self._sync_author_detail()
  931.  
  932.     _end_managingeditor = _end_author
  933.     _end_dc_author = _end_author
  934.     _end_dc_creator = _end_author
  935.     _end_itunes_author = _end_author
  936.     
  937.     def _start_itunes_owner(self, attrsD):
  938.         self.inpublisher = 1
  939.         self.push('publisher', 0)
  940.  
  941.     
  942.     def _end_itunes_owner(self):
  943.         self.pop('publisher')
  944.         self.inpublisher = 0
  945.         self._sync_author_detail('publisher')
  946.  
  947.     
  948.     def _start_contributor(self, attrsD):
  949.         self.incontributor = 1
  950.         context = self._getContext()
  951.         context.setdefault('contributors', [])
  952.         context['contributors'].append(FeedParserDict())
  953.         self.push('contributor', 0)
  954.  
  955.     
  956.     def _end_contributor(self):
  957.         self.pop('contributor')
  958.         self.incontributor = 0
  959.  
  960.     
  961.     def _start_dc_contributor(self, attrsD):
  962.         self.incontributor = 1
  963.         context = self._getContext()
  964.         context.setdefault('contributors', [])
  965.         context['contributors'].append(FeedParserDict())
  966.         self.push('name', 0)
  967.  
  968.     
  969.     def _end_dc_contributor(self):
  970.         self._end_name()
  971.         self.incontributor = 0
  972.  
  973.     
  974.     def _start_name(self, attrsD):
  975.         self.push('name', 0)
  976.  
  977.     _start_itunes_name = _start_name
  978.     
  979.     def _end_name(self):
  980.         value = self.pop('name')
  981.         if self.inpublisher:
  982.             self._save_author('name', value, 'publisher')
  983.         elif self.inauthor:
  984.             self._save_author('name', value)
  985.         elif self.incontributor:
  986.             self._save_contributor('name', value)
  987.         elif self.intextinput:
  988.             context = self._getContext()
  989.             context['textinput']['name'] = value
  990.         
  991.  
  992.     _end_itunes_name = _end_name
  993.     
  994.     def _start_width(self, attrsD):
  995.         self.push('width', 0)
  996.  
  997.     
  998.     def _end_width(self):
  999.         value = self.pop('width')
  1000.         
  1001.         try:
  1002.             value = int(value)
  1003.         except:
  1004.             value = 0
  1005.  
  1006.         if self.inimage:
  1007.             context = self._getContext()
  1008.             context['image']['width'] = value
  1009.         
  1010.  
  1011.     
  1012.     def _start_height(self, attrsD):
  1013.         self.push('height', 0)
  1014.  
  1015.     
  1016.     def _end_height(self):
  1017.         value = self.pop('height')
  1018.         
  1019.         try:
  1020.             value = int(value)
  1021.         except:
  1022.             value = 0
  1023.  
  1024.         if self.inimage:
  1025.             context = self._getContext()
  1026.             context['image']['height'] = value
  1027.         
  1028.  
  1029.     
  1030.     def _start_url(self, attrsD):
  1031.         self.push('href', 1)
  1032.  
  1033.     _start_homepage = _start_url
  1034.     _start_uri = _start_url
  1035.     
  1036.     def _end_url(self):
  1037.         value = self.pop('href')
  1038.         if self.inauthor:
  1039.             self._save_author('href', value)
  1040.         elif self.incontributor:
  1041.             self._save_contributor('href', value)
  1042.         elif self.inimage:
  1043.             context = self._getContext()
  1044.             context['image']['href'] = value
  1045.         elif self.intextinput:
  1046.             context = self._getContext()
  1047.             context['textinput']['link'] = value
  1048.         
  1049.  
  1050.     _end_homepage = _end_url
  1051.     _end_uri = _end_url
  1052.     
  1053.     def _start_email(self, attrsD):
  1054.         self.push('email', 0)
  1055.  
  1056.     _start_itunes_email = _start_email
  1057.     
  1058.     def _end_email(self):
  1059.         value = self.pop('email')
  1060.         if self.inpublisher:
  1061.             self._save_author('email', value, 'publisher')
  1062.         elif self.inauthor:
  1063.             self._save_author('email', value)
  1064.         elif self.incontributor:
  1065.             self._save_contributor('email', value)
  1066.         
  1067.  
  1068.     _end_itunes_email = _end_email
  1069.     
  1070.     def _getContext(self):
  1071.         if self.insource:
  1072.             context = self.sourcedata
  1073.         elif self.inentry:
  1074.             context = self.entries[-1]
  1075.         else:
  1076.             context = self.feeddata
  1077.         return context
  1078.  
  1079.     
  1080.     def _save_author(self, key, value, prefix = 'author'):
  1081.         context = self._getContext()
  1082.         context.setdefault(prefix + '_detail', FeedParserDict())
  1083.         context[prefix + '_detail'][key] = value
  1084.         self._sync_author_detail()
  1085.  
  1086.     
  1087.     def _save_contributor(self, key, value):
  1088.         context = self._getContext()
  1089.         context.setdefault('contributors', [
  1090.             FeedParserDict()])
  1091.         context['contributors'][-1][key] = value
  1092.  
  1093.     
  1094.     def _sync_author_detail(self, key = 'author'):
  1095.         context = self._getContext()
  1096.         detail = context.get('%s_detail' % key)
  1097.         if detail:
  1098.             name = detail.get('name')
  1099.             email = detail.get('email')
  1100.             if name and email:
  1101.                 context[key] = '%s (%s)' % (name, email)
  1102.             elif name:
  1103.                 context[key] = name
  1104.             elif email:
  1105.                 context[key] = email
  1106.             
  1107.         else:
  1108.             author = context.get(key)
  1109.             if not author:
  1110.                 return None
  1111.             
  1112.             emailmatch = re.search('(([a-zA-Z0-9\\_\\-\\.\\+]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?))', author)
  1113.             if not emailmatch:
  1114.                 return None
  1115.             
  1116.             email = emailmatch.group(0)
  1117.             author = author.replace(email, '')
  1118.             author = author.replace('()', '')
  1119.             author = author.strip()
  1120.             if author and author[0] == '(':
  1121.                 author = author[1:]
  1122.             
  1123.             if author and author[-1] == ')':
  1124.                 author = author[:-1]
  1125.             
  1126.             author = author.strip()
  1127.             context.setdefault('%s_detail' % key, FeedParserDict())
  1128.             context['%s_detail' % key]['name'] = author
  1129.             context['%s_detail' % key]['email'] = email
  1130.  
  1131.     
  1132.     def _start_subtitle(self, attrsD):
  1133.         self.pushContent('subtitle', attrsD, 'text/plain', 1)
  1134.  
  1135.     _start_tagline = _start_subtitle
  1136.     _start_itunes_subtitle = _start_subtitle
  1137.     
  1138.     def _end_subtitle(self):
  1139.         self.popContent('subtitle')
  1140.  
  1141.     _end_tagline = _end_subtitle
  1142.     _end_itunes_subtitle = _end_subtitle
  1143.     
  1144.     def _start_rights(self, attrsD):
  1145.         self.pushContent('rights', attrsD, 'text/plain', 1)
  1146.  
  1147.     _start_dc_rights = _start_rights
  1148.     _start_copyright = _start_rights
  1149.     
  1150.     def _end_rights(self):
  1151.         self.popContent('rights')
  1152.  
  1153.     _end_dc_rights = _end_rights
  1154.     _end_copyright = _end_rights
  1155.     
  1156.     def _start_item(self, attrsD):
  1157.         self.entries.append(FeedParserDict())
  1158.         self.push('item', 0)
  1159.         self.inentry = 1
  1160.         self.guidislink = 0
  1161.         id = self._getAttribute(attrsD, 'rdf:about')
  1162.         if id:
  1163.             context = self._getContext()
  1164.             context['id'] = id
  1165.         
  1166.         self._cdf_common(attrsD)
  1167.  
  1168.     _start_entry = _start_item
  1169.     _start_product = _start_item
  1170.     
  1171.     def _end_item(self):
  1172.         self.pop('item')
  1173.         self.inentry = 0
  1174.  
  1175.     _end_entry = _end_item
  1176.     
  1177.     def _start_dc_language(self, attrsD):
  1178.         self.push('language', 1)
  1179.  
  1180.     _start_language = _start_dc_language
  1181.     
  1182.     def _end_dc_language(self):
  1183.         self.lang = self.pop('language')
  1184.  
  1185.     _end_language = _end_dc_language
  1186.     
  1187.     def _start_dc_publisher(self, attrsD):
  1188.         self.push('publisher', 1)
  1189.  
  1190.     _start_webmaster = _start_dc_publisher
  1191.     
  1192.     def _end_dc_publisher(self):
  1193.         self.pop('publisher')
  1194.         self._sync_author_detail('publisher')
  1195.  
  1196.     _end_webmaster = _end_dc_publisher
  1197.     
  1198.     def _start_published(self, attrsD):
  1199.         self.push('published', 1)
  1200.  
  1201.     _start_dcterms_issued = _start_published
  1202.     _start_issued = _start_published
  1203.     
  1204.     def _end_published(self):
  1205.         value = self.pop('published')
  1206.         self._save('published_parsed', _parse_date(value))
  1207.  
  1208.     _end_dcterms_issued = _end_published
  1209.     _end_issued = _end_published
  1210.     
  1211.     def _start_updated(self, attrsD):
  1212.         self.push('updated', 1)
  1213.  
  1214.     _start_modified = _start_updated
  1215.     _start_dcterms_modified = _start_updated
  1216.     _start_pubdate = _start_updated
  1217.     _start_dc_date = _start_updated
  1218.     
  1219.     def _end_updated(self):
  1220.         value = self.pop('updated')
  1221.         parsed_value = _parse_date(value)
  1222.         self._save('updated_parsed', parsed_value)
  1223.  
  1224.     _end_modified = _end_updated
  1225.     _end_dcterms_modified = _end_updated
  1226.     _end_pubdate = _end_updated
  1227.     _end_dc_date = _end_updated
  1228.     
  1229.     def _start_created(self, attrsD):
  1230.         self.push('created', 1)
  1231.  
  1232.     _start_dcterms_created = _start_created
  1233.     
  1234.     def _end_created(self):
  1235.         value = self.pop('created')
  1236.         self._save('created_parsed', _parse_date(value))
  1237.  
  1238.     _end_dcterms_created = _end_created
  1239.     
  1240.     def _start_expirationdate(self, attrsD):
  1241.         self.push('expired', 1)
  1242.  
  1243.     
  1244.     def _end_expirationdate(self):
  1245.         self._save('expired_parsed', _parse_date(self.pop('expired')))
  1246.  
  1247.     
  1248.     def _start_cc_license(self, attrsD):
  1249.         self.push('license', 1)
  1250.         value = self._getAttribute(attrsD, 'rdf:resource')
  1251.         if value:
  1252.             self.elementstack[-1][2].append(value)
  1253.         
  1254.         self.pop('license')
  1255.  
  1256.     
  1257.     def _start_creativecommons_license(self, attrsD):
  1258.         self.push('license', 1)
  1259.  
  1260.     
  1261.     def _end_creativecommons_license(self):
  1262.         self.pop('license')
  1263.  
  1264.     
  1265.     def _addTag(self, term, scheme, label):
  1266.         context = self._getContext()
  1267.         tags = context.setdefault('tags', [])
  1268.         if not term and not scheme and not label:
  1269.             return None
  1270.         
  1271.         value = FeedParserDict({
  1272.             'term': term,
  1273.             'scheme': scheme,
  1274.             'label': label })
  1275.         if value not in tags:
  1276.             tags.append(FeedParserDict({
  1277.                 'term': term,
  1278.                 'scheme': scheme,
  1279.                 'label': label }))
  1280.         
  1281.  
  1282.     
  1283.     def _start_category(self, attrsD):
  1284.         if _debug:
  1285.             sys.stderr.write('entering _start_category with %s\n' % repr(attrsD))
  1286.         
  1287.         term = attrsD.get('term')
  1288.         scheme = attrsD.get('scheme', attrsD.get('domain'))
  1289.         label = attrsD.get('label')
  1290.         self._addTag(term, scheme, label)
  1291.         self.push('category', 1)
  1292.  
  1293.     _start_dc_subject = _start_category
  1294.     _start_keywords = _start_category
  1295.     
  1296.     def _end_itunes_keywords(self):
  1297.         for term in self.pop('itunes_keywords').split():
  1298.             self._addTag(term, 'http://www.itunes.com/', None)
  1299.         
  1300.  
  1301.     
  1302.     def _start_itunes_category(self, attrsD):
  1303.         self._addTag(attrsD.get('text'), 'http://www.itunes.com/', None)
  1304.         self.push('category', 1)
  1305.  
  1306.     
  1307.     def _end_category(self):
  1308.         value = self.pop('category')
  1309.         if not value:
  1310.             return None
  1311.         
  1312.         context = self._getContext()
  1313.         tags = context['tags']
  1314.         if value and len(tags) and not tags[-1]['term']:
  1315.             tags[-1]['term'] = value
  1316.         else:
  1317.             self._addTag(value, None, None)
  1318.  
  1319.     _end_dc_subject = _end_category
  1320.     _end_keywords = _end_category
  1321.     _end_itunes_category = _end_category
  1322.     
  1323.     def _start_cloud(self, attrsD):
  1324.         self._getContext()['cloud'] = FeedParserDict(attrsD)
  1325.  
  1326.     
  1327.     def _start_link(self, attrsD):
  1328.         attrsD.setdefault('rel', 'alternate')
  1329.         attrsD.setdefault('type', 'text/html')
  1330.         attrsD = self._itsAnHrefDamnIt(attrsD)
  1331.         if attrsD.has_key('href'):
  1332.             attrsD['href'] = self.resolveURI(attrsD['href'])
  1333.         
  1334.         if not self.infeed and self.inentry:
  1335.             pass
  1336.         expectingText = self.insource
  1337.         context = self._getContext()
  1338.         context.setdefault('links', [])
  1339.         context['links'].append(FeedParserDict(attrsD))
  1340.         if attrsD['rel'] == 'enclosure':
  1341.             self._start_enclosure(attrsD)
  1342.         
  1343.         if attrsD.has_key('href'):
  1344.             expectingText = 0
  1345.             if attrsD.get('rel') == 'alternate' and self.mapContentType(attrsD.get('type')) in self.html_types:
  1346.                 context['link'] = attrsD['href']
  1347.             
  1348.         else:
  1349.             self.push('link', expectingText)
  1350.  
  1351.     _start_producturl = _start_link
  1352.     
  1353.     def _end_link(self):
  1354.         value = self.pop('link')
  1355.         context = self._getContext()
  1356.         if self.intextinput:
  1357.             context['textinput']['link'] = value
  1358.         
  1359.         if self.inimage:
  1360.             context['image']['link'] = value
  1361.         
  1362.  
  1363.     _end_producturl = _end_link
  1364.     
  1365.     def _start_guid(self, attrsD):
  1366.         self.guidislink = attrsD.get('ispermalink', 'true') == 'true'
  1367.         self.push('id', 1)
  1368.  
  1369.     
  1370.     def _end_guid(self):
  1371.         value = self.pop('id')
  1372.         if self.guidislink:
  1373.             pass
  1374.         self._save('guidislink', not self._getContext().has_key('link'))
  1375.         if self.guidislink:
  1376.             self._save('link', value)
  1377.         
  1378.  
  1379.     
  1380.     def _start_title(self, attrsD):
  1381.         if not self.infeed and self.inentry:
  1382.             pass
  1383.         self.pushContent('title', attrsD, 'text/plain', self.insource)
  1384.  
  1385.     _start_dc_title = _start_title
  1386.     _start_media_title = _start_title
  1387.     
  1388.     def _end_title(self):
  1389.         value = self.popContent('title')
  1390.         context = self._getContext()
  1391.         if self.intextinput:
  1392.             context['textinput']['title'] = value
  1393.         elif self.inimage:
  1394.             context['image']['title'] = value
  1395.         
  1396.  
  1397.     _end_dc_title = _end_title
  1398.     _end_media_title = _end_title
  1399.     
  1400.     def _start_description(self, attrsD):
  1401.         context = self._getContext()
  1402.         None(self.pushContent, 'description', attrsD, 'text/html' if context.has_key('summary') else self.insource)
  1403.  
  1404.     
  1405.     def _start_abstract(self, attrsD):
  1406.         if not self.infeed and self.inentry:
  1407.             pass
  1408.         self.pushContent('description', attrsD, 'text/plain', self.insource)
  1409.  
  1410.     
  1411.     def _end_description(self):
  1412.         if self._summaryKey == 'content':
  1413.             self._end_content()
  1414.         else:
  1415.             value = self.popContent('description')
  1416.             context = self._getContext()
  1417.             if self.intextinput:
  1418.                 context['textinput']['description'] = value
  1419.             elif self.inimage:
  1420.                 context['image']['description'] = value
  1421.             
  1422.         self._summaryKey = None
  1423.  
  1424.     _end_abstract = _end_description
  1425.     
  1426.     def _start_info(self, attrsD):
  1427.         self.pushContent('info', attrsD, 'text/plain', 1)
  1428.  
  1429.     _start_feedburner_browserfriendly = _start_info
  1430.     
  1431.     def _end_info(self):
  1432.         self.popContent('info')
  1433.  
  1434.     _end_feedburner_browserfriendly = _end_info
  1435.     
  1436.     def _start_generator(self, attrsD):
  1437.         if attrsD:
  1438.             attrsD = self._itsAnHrefDamnIt(attrsD)
  1439.             if attrsD.has_key('href'):
  1440.                 attrsD['href'] = self.resolveURI(attrsD['href'])
  1441.             
  1442.         
  1443.         self._getContext()['generator_detail'] = FeedParserDict(attrsD)
  1444.         self.push('generator', 1)
  1445.  
  1446.     
  1447.     def _end_generator(self):
  1448.         value = self.pop('generator')
  1449.         context = self._getContext()
  1450.         if context.has_key('generator_detail'):
  1451.             context['generator_detail']['name'] = value
  1452.         
  1453.  
  1454.     
  1455.     def _start_admin_generatoragent(self, attrsD):
  1456.         self.push('generator', 1)
  1457.         value = self._getAttribute(attrsD, 'rdf:resource')
  1458.         if value:
  1459.             self.elementstack[-1][2].append(value)
  1460.         
  1461.         self.pop('generator')
  1462.         self._getContext()['generator_detail'] = FeedParserDict({
  1463.             'href': value })
  1464.  
  1465.     
  1466.     def _start_admin_errorreportsto(self, attrsD):
  1467.         self.push('errorreportsto', 1)
  1468.         value = self._getAttribute(attrsD, 'rdf:resource')
  1469.         if value:
  1470.             self.elementstack[-1][2].append(value)
  1471.         
  1472.         self.pop('errorreportsto')
  1473.  
  1474.     
  1475.     def _start_summary(self, attrsD):
  1476.         context = self._getContext()
  1477.         if context.has_key('summary'):
  1478.             self._summaryKey = 'content'
  1479.             self._start_content(attrsD)
  1480.         else:
  1481.             self._summaryKey = 'summary'
  1482.             self.pushContent(self._summaryKey, attrsD, 'text/plain', 1)
  1483.  
  1484.     _start_itunes_summary = _start_summary
  1485.     
  1486.     def _end_summary(self):
  1487.         None(self.popContent if self._summaryKey == 'content' else 'summary')
  1488.         self._summaryKey = None
  1489.  
  1490.     _end_itunes_summary = _end_summary
  1491.     
  1492.     def _start_enclosure(self, attrsD):
  1493.         attrsD = self._itsAnHrefDamnIt(attrsD)
  1494.         self._getContext().setdefault('enclosures', []).append(FeedParserDict(attrsD))
  1495.         href = attrsD.get('href')
  1496.         if href:
  1497.             context = self._getContext()
  1498.             if not context.get('id'):
  1499.                 context['id'] = href
  1500.             
  1501.         
  1502.  
  1503.     
  1504.     def _start_source(self, attrsD):
  1505.         self.insource = 1
  1506.  
  1507.     
  1508.     def _end_source(self):
  1509.         self.insource = 0
  1510.         self._getContext()['source'] = copy.deepcopy(self.sourcedata)
  1511.         self.sourcedata.clear()
  1512.  
  1513.     
  1514.     def _start_content(self, attrsD):
  1515.         self.pushContent('content', attrsD, 'text/plain', 1)
  1516.         src = attrsD.get('src')
  1517.         if src:
  1518.             self.contentparams['src'] = src
  1519.         
  1520.         self.push('content', 1)
  1521.  
  1522.     
  1523.     def _start_prodlink(self, attrsD):
  1524.         self.pushContent('content', attrsD, 'text/html', 1)
  1525.  
  1526.     
  1527.     def _start_body(self, attrsD):
  1528.         self.pushContent('content', attrsD, 'application/xhtml+xml', 1)
  1529.  
  1530.     _start_xhtml_body = _start_body
  1531.     
  1532.     def _start_content_encoded(self, attrsD):
  1533.         self.pushContent('content', attrsD, 'text/html', 1)
  1534.  
  1535.     _start_fullitem = _start_content_encoded
  1536.     
  1537.     def _end_content(self):
  1538.         copyToDescription = self.mapContentType(self.contentparams.get('type')) in [
  1539.             'text/plain'] + self.html_types
  1540.         value = self.popContent('content')
  1541.         if copyToDescription:
  1542.             self._save('description', value)
  1543.         
  1544.  
  1545.     _end_body = _end_content
  1546.     _end_xhtml_body = _end_content
  1547.     _end_content_encoded = _end_content
  1548.     _end_fullitem = _end_content
  1549.     _end_prodlink = _end_content
  1550.     
  1551.     def _start_itunes_image(self, attrsD):
  1552.         self.push('itunes_image', 0)
  1553.         self._getContext()['image'] = FeedParserDict({
  1554.             'href': attrsD.get('href') })
  1555.  
  1556.     _start_itunes_link = _start_itunes_image
  1557.     
  1558.     def _end_itunes_block(self):
  1559.         value = self.pop('itunes_block', 0)
  1560.         if not value == 'yes' or 1:
  1561.             pass
  1562.         self._getContext()['itunes_block'] = 0
  1563.  
  1564.     
  1565.     def _end_itunes_explicit(self):
  1566.         value = self.pop('itunes_explicit', 0)
  1567.         if not value == 'yes' or 1:
  1568.             pass
  1569.         self._getContext()['itunes_explicit'] = 0
  1570.  
  1571.  
  1572. if _XML_AVAILABLE:
  1573.     
  1574.     class _StrictFeedParser(_FeedParserMixin, xml.sax.handler.ContentHandler):
  1575.         
  1576.         def __init__(self, baseuri, baselang, encoding):
  1577.             if _debug:
  1578.                 sys.stderr.write('trying StrictFeedParser\n')
  1579.             
  1580.             xml.sax.handler.ContentHandler.__init__(self)
  1581.             _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
  1582.             self.bozo = 0
  1583.             self.exc = None
  1584.  
  1585.         
  1586.         def startPrefixMapping(self, prefix, uri):
  1587.             self.trackNamespace(prefix, uri)
  1588.  
  1589.         
  1590.         def startElementNS(self, name, qname, attrs):
  1591.             (namespace, localname) = name
  1592.             if not namespace:
  1593.                 pass
  1594.             lowernamespace = str('').lower()
  1595.             if lowernamespace.find('backend.userland.com/rss') != -1:
  1596.                 namespace = 'http://backend.userland.com/rss'
  1597.                 lowernamespace = namespace
  1598.             
  1599.             if qname and qname.find(':') > 0:
  1600.                 givenprefix = qname.split(':')[0]
  1601.             else:
  1602.                 givenprefix = None
  1603.             prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
  1604.             if givenprefix:
  1605.                 if (prefix == None or prefix == '' or lowernamespace == '') and not self.namespacesInUse.has_key(givenprefix):
  1606.                     raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix
  1607.                 
  1608.             if prefix:
  1609.                 localname = prefix + ':' + localname
  1610.             
  1611.             localname = str(localname).lower()
  1612.             if _debug:
  1613.                 sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, attrs.items(), localname))
  1614.             
  1615.             attrsD = { }
  1616.             for namespace, attrlocalname in attrs._attrs.items():
  1617.                 attrvalue = None
  1618.                 if not namespace:
  1619.                     pass
  1620.                 lowernamespace = ''.lower()
  1621.                 prefix = self._matchnamespaces.get(lowernamespace, '')
  1622.                 if prefix:
  1623.                     attrlocalname = prefix + ':' + attrlocalname
  1624.                 
  1625.                 attrsD[str(attrlocalname).lower()] = attrvalue
  1626.             
  1627.             for qname in attrs.getQNames():
  1628.                 attrsD[str(qname).lower()] = attrs.getValueByQName(qname)
  1629.             
  1630.             self.unknown_starttag(localname, attrsD.items())
  1631.  
  1632.         
  1633.         def characters(self, text):
  1634.             self.handle_data(text)
  1635.  
  1636.         
  1637.         def endElementNS(self, name, qname):
  1638.             (namespace, localname) = name
  1639.             if not namespace:
  1640.                 pass
  1641.             lowernamespace = str('').lower()
  1642.             if qname and qname.find(':') > 0:
  1643.                 givenprefix = qname.split(':')[0]
  1644.             else:
  1645.                 givenprefix = ''
  1646.             prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
  1647.             if prefix:
  1648.                 localname = prefix + ':' + localname
  1649.             
  1650.             localname = str(localname).lower()
  1651.             self.unknown_endtag(localname)
  1652.  
  1653.         
  1654.         def error(self, exc):
  1655.             self.bozo = 1
  1656.             self.exc = exc
  1657.  
  1658.         
  1659.         def fatalError(self, exc):
  1660.             self.error(exc)
  1661.             raise exc
  1662.  
  1663.  
  1664.  
  1665.  
  1666. class _BaseHTMLProcessor(sgmllib.SGMLParser):
  1667.     elements_no_end_tag = [
  1668.         'area',
  1669.         'base',
  1670.         'basefont',
  1671.         'br',
  1672.         'col',
  1673.         'frame',
  1674.         'hr',
  1675.         'img',
  1676.         'input',
  1677.         'isindex',
  1678.         'link',
  1679.         'meta',
  1680.         'param']
  1681.     
  1682.     def __init__(self, encoding):
  1683.         self.encoding = encoding
  1684.         if _debug:
  1685.             sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
  1686.         
  1687.         sgmllib.SGMLParser.__init__(self)
  1688.  
  1689.     
  1690.     def reset(self):
  1691.         self.pieces = []
  1692.         sgmllib.SGMLParser.reset(self)
  1693.  
  1694.     
  1695.     def _shorttag_replace(self, match):
  1696.         tag = match.group(1)
  1697.         if tag in self.elements_no_end_tag:
  1698.             return '<' + tag + ' />'
  1699.         else:
  1700.             return '<' + tag + '></' + tag + '>'
  1701.  
  1702.     
  1703.     def feed(self, data):
  1704.         data = re.compile('<!((?!DOCTYPE|--|\\[))', re.IGNORECASE).sub('<!\\1', data)
  1705.         data = re.sub('<([^<\\s]+?)\\s*/>', self._shorttag_replace, data)
  1706.         data = data.replace(''', "'")
  1707.         data = data.replace('"', '"')
  1708.         if self.encoding and type(data) == type(u''):
  1709.             data = data.encode(self.encoding)
  1710.         
  1711.         sgmllib.SGMLParser.feed(self, data)
  1712.  
  1713.     
  1714.     def normalize_attrs(self, attrs):
  1715.         attrs = [ (k.lower(), v) for k, v in attrs ]
  1716.         attrs = [ (k, v) for k, v in attrs ]
  1717.         return attrs
  1718.  
  1719.     
  1720.     def unknown_starttag(self, tag, attrs):
  1721.         if _debug:
  1722.             sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
  1723.         
  1724.         uattrs = []
  1725.         for key, value in attrs:
  1726.             if type(value) != type(u''):
  1727.                 value = unicode(value, self.encoding)
  1728.             
  1729.             uattrs.append((unicode(key, self.encoding), value))
  1730.         
  1731.         strattrs = []([ u' %s="%s"' % (key, value) for key, value in uattrs ]).encode(self.encoding)
  1732.  
  1733.     
  1734.     def unknown_endtag(self, tag):
  1735.         if tag not in self.elements_no_end_tag:
  1736.             self.pieces.append('</%(tag)s>' % locals())
  1737.         
  1738.  
  1739.     
  1740.     def handle_charref(self, ref):
  1741.         self.pieces.append('&#%(ref)s;' % locals())
  1742.  
  1743.     
  1744.     def handle_entityref(self, ref):
  1745.         self.pieces.append('&%(ref)s;' % locals())
  1746.  
  1747.     
  1748.     def handle_data(self, text):
  1749.         if _debug:
  1750.             sys.stderr.write('_BaseHTMLProcessor, handle_text, text=%s\n' % text)
  1751.         
  1752.         self.pieces.append(text)
  1753.  
  1754.     
  1755.     def handle_comment(self, text):
  1756.         self.pieces.append('<!--%(text)s-->' % locals())
  1757.  
  1758.     
  1759.     def handle_pi(self, text):
  1760.         self.pieces.append('<?%(text)s>' % locals())
  1761.  
  1762.     
  1763.     def handle_decl(self, text):
  1764.         self.pieces.append('<!%(text)s>' % locals())
  1765.  
  1766.     _new_declname_match = re.compile('[a-zA-Z][-_.a-zA-Z0-9:]*\\s*').match
  1767.     
  1768.     def _scan_name(self, i, declstartpos):
  1769.         rawdata = self.rawdata
  1770.         n = len(rawdata)
  1771.         if i == n:
  1772.             return (None, -1)
  1773.         
  1774.         m = self._new_declname_match(rawdata, i)
  1775.         if m:
  1776.             s = m.group()
  1777.             name = s.strip()
  1778.             if i + len(s) == n:
  1779.                 return (None, -1)
  1780.             
  1781.             return (name.lower(), m.end())
  1782.         else:
  1783.             self.handle_data(rawdata)
  1784.             return (None, -1)
  1785.  
  1786.     
  1787.     def output(self):
  1788.         return []([ str(p) for p in self.pieces ])
  1789.  
  1790.  
  1791.  
  1792. class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
  1793.     
  1794.     def __init__(self, baseuri, baselang, encoding):
  1795.         sgmllib.SGMLParser.__init__(self)
  1796.         _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
  1797.  
  1798.     
  1799.     def decodeEntities(self, element, data):
  1800.         data = data.replace('<', '<')
  1801.         data = data.replace('<', '<')
  1802.         data = data.replace('>', '>')
  1803.         data = data.replace('>', '>')
  1804.         data = data.replace('&', '&')
  1805.         data = data.replace('&', '&')
  1806.         data = data.replace('"', '"')
  1807.         data = data.replace('"', '"')
  1808.         data = data.replace(''', ''')
  1809.         data = data.replace(''', ''')
  1810.         if self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
  1811.             data = data.replace('<', '<')
  1812.             data = data.replace('>', '>')
  1813.             data = data.replace('&', '&')
  1814.             data = data.replace('"', '"')
  1815.             data = data.replace(''', "'")
  1816.         
  1817.         return data
  1818.  
  1819.  
  1820.  
  1821. class _RelativeURIResolver(_BaseHTMLProcessor):
  1822.     relative_uris = [
  1823.         ('a', 'href'),
  1824.         ('applet', 'codebase'),
  1825.         ('area', 'href'),
  1826.         ('blockquote', 'cite'),
  1827.         ('body', 'background'),
  1828.         ('del', 'cite'),
  1829.         ('form', 'action'),
  1830.         ('frame', 'longdesc'),
  1831.         ('frame', 'src'),
  1832.         ('iframe', 'longdesc'),
  1833.         ('iframe', 'src'),
  1834.         ('head', 'profile'),
  1835.         ('img', 'longdesc'),
  1836.         ('img', 'src'),
  1837.         ('img', 'usemap'),
  1838.         ('input', 'src'),
  1839.         ('input', 'usemap'),
  1840.         ('ins', 'cite'),
  1841.         ('link', 'href'),
  1842.         ('object', 'classid'),
  1843.         ('object', 'codebase'),
  1844.         ('object', 'data'),
  1845.         ('object', 'usemap'),
  1846.         ('q', 'cite'),
  1847.         ('script', 'src')]
  1848.     
  1849.     def __init__(self, baseuri, encoding):
  1850.         _BaseHTMLProcessor.__init__(self, encoding)
  1851.         self.baseuri = baseuri
  1852.  
  1853.     
  1854.     def resolveURI(self, uri):
  1855.         return _urljoin(self.baseuri, uri)
  1856.  
  1857.     
  1858.     def unknown_starttag(self, tag, attrs):
  1859.         attrs = self.normalize_attrs(attrs)
  1860.         attrs = [ (key, value) for key, value in attrs ]
  1861.         _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
  1862.  
  1863.  
  1864.  
  1865. def _resolveRelativeURIs(htmlSource, baseURI, encoding):
  1866.     if _debug:
  1867.         sys.stderr.write('entering _resolveRelativeURIs\n')
  1868.     
  1869.     p = _RelativeURIResolver(baseURI, encoding)
  1870.     p.feed(htmlSource)
  1871.     return p.output()
  1872.  
  1873.  
  1874. class _HTMLSanitizer(_BaseHTMLProcessor):
  1875.     acceptable_elements = [
  1876.         'a',
  1877.         'abbr',
  1878.         'acronym',
  1879.         'address',
  1880.         'area',
  1881.         'b',
  1882.         'big',
  1883.         'blockquote',
  1884.         'br',
  1885.         'button',
  1886.         'caption',
  1887.         'center',
  1888.         'cite',
  1889.         'code',
  1890.         'col',
  1891.         'colgroup',
  1892.         'dd',
  1893.         'del',
  1894.         'dfn',
  1895.         'dir',
  1896.         'div',
  1897.         'dl',
  1898.         'dt',
  1899.         'em',
  1900.         'fieldset',
  1901.         'font',
  1902.         'form',
  1903.         'h1',
  1904.         'h2',
  1905.         'h3',
  1906.         'h4',
  1907.         'h5',
  1908.         'h6',
  1909.         'hr',
  1910.         'i',
  1911.         'img',
  1912.         'input',
  1913.         'ins',
  1914.         'kbd',
  1915.         'label',
  1916.         'legend',
  1917.         'li',
  1918.         'map',
  1919.         'menu',
  1920.         'ol',
  1921.         'optgroup',
  1922.         'option',
  1923.         'p',
  1924.         'pre',
  1925.         'q',
  1926.         's',
  1927.         'samp',
  1928.         'select',
  1929.         'small',
  1930.         'span',
  1931.         'strike',
  1932.         'strong',
  1933.         'sub',
  1934.         'sup',
  1935.         'table',
  1936.         'tbody',
  1937.         'td',
  1938.         'textarea',
  1939.         'tfoot',
  1940.         'th',
  1941.         'thead',
  1942.         'tr',
  1943.         'tt',
  1944.         'u',
  1945.         'ul',
  1946.         'var']
  1947.     acceptable_attributes = [
  1948.         'abbr',
  1949.         'accept',
  1950.         'accept-charset',
  1951.         'accesskey',
  1952.         'action',
  1953.         'align',
  1954.         'alt',
  1955.         'axis',
  1956.         'border',
  1957.         'cellpadding',
  1958.         'cellspacing',
  1959.         'char',
  1960.         'charoff',
  1961.         'charset',
  1962.         'checked',
  1963.         'cite',
  1964.         'class',
  1965.         'clear',
  1966.         'cols',
  1967.         'colspan',
  1968.         'color',
  1969.         'compact',
  1970.         'coords',
  1971.         'datetime',
  1972.         'dir',
  1973.         'disabled',
  1974.         'enctype',
  1975.         'for',
  1976.         'frame',
  1977.         'headers',
  1978.         'height',
  1979.         'href',
  1980.         'hreflang',
  1981.         'hspace',
  1982.         'id',
  1983.         'ismap',
  1984.         'label',
  1985.         'lang',
  1986.         'longdesc',
  1987.         'maxlength',
  1988.         'media',
  1989.         'method',
  1990.         'multiple',
  1991.         'name',
  1992.         'nohref',
  1993.         'noshade',
  1994.         'nowrap',
  1995.         'prompt',
  1996.         'readonly',
  1997.         'rel',
  1998.         'rev',
  1999.         'rows',
  2000.         'rowspan',
  2001.         'rules',
  2002.         'scope',
  2003.         'selected',
  2004.         'shape',
  2005.         'size',
  2006.         'span',
  2007.         'src',
  2008.         'start',
  2009.         'summary',
  2010.         'tabindex',
  2011.         'target',
  2012.         'title',
  2013.         'type',
  2014.         'usemap',
  2015.         'valign',
  2016.         'value',
  2017.         'vspace',
  2018.         'width']
  2019.     unacceptable_elements_with_end_tag = [
  2020.         'script',
  2021.         'applet']
  2022.     
  2023.     def reset(self):
  2024.         _BaseHTMLProcessor.reset(self)
  2025.         self.unacceptablestack = 0
  2026.  
  2027.     
  2028.     def unknown_starttag(self, tag, attrs):
  2029.         if tag not in self.acceptable_elements:
  2030.             if tag in self.unacceptable_elements_with_end_tag:
  2031.                 self.unacceptablestack += 1
  2032.             
  2033.             return None
  2034.         
  2035.         attrs = self.normalize_attrs(attrs)
  2036.         attrs = _[1]
  2037.         _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
  2038.  
  2039.     
  2040.     def unknown_endtag(self, tag):
  2041.         if tag not in self.acceptable_elements:
  2042.             if tag in self.unacceptable_elements_with_end_tag:
  2043.                 self.unacceptablestack -= 1
  2044.             
  2045.             return None
  2046.         
  2047.         _BaseHTMLProcessor.unknown_endtag(self, tag)
  2048.  
  2049.     
  2050.     def handle_pi(self, text):
  2051.         pass
  2052.  
  2053.     
  2054.     def handle_decl(self, text):
  2055.         pass
  2056.  
  2057.     
  2058.     def handle_data(self, text):
  2059.         if not self.unacceptablestack:
  2060.             _BaseHTMLProcessor.handle_data(self, text)
  2061.         
  2062.  
  2063.  
  2064.  
  2065. def _sanitizeHTML(htmlSource, encoding):
  2066.     p = _HTMLSanitizer(encoding)
  2067.     p.feed(htmlSource)
  2068.     data = p.output()
  2069.     if TIDY_MARKUP:
  2070.         _tidy = None
  2071.         for tidy_interface in PREFERRED_TIDY_INTERFACES:
  2072.             
  2073.             try:
  2074.                 if tidy_interface == 'uTidy':
  2075.                     _utidy = parseString
  2076.                     import tidy
  2077.                     
  2078.                     def _tidy(data, **kwargs):
  2079.                         return str(_utidy(data, **kwargs))
  2080.  
  2081.                     break
  2082.                 elif tidy_interface == 'mxTidy':
  2083.                     _mxtidy = Tidy
  2084.                     import mx.Tidy
  2085.                     
  2086.                     def _tidy(data, **kwargs):
  2087.                         (nerrors, nwarnings, data, errordata) = _mxtidy.tidy(data, **kwargs)
  2088.                         return data
  2089.  
  2090.                     break
  2091.             continue
  2092.             continue
  2093.  
  2094.         
  2095.         if _tidy:
  2096.             utf8 = type(data) == type(u'')
  2097.             if utf8:
  2098.                 data = data.encode('utf-8')
  2099.             
  2100.             data = _tidy(data, output_xhtml = 1, numeric_entities = 1, wrap = 0, char_encoding = 'utf8')
  2101.             if utf8:
  2102.                 data = unicode(data, 'utf-8')
  2103.             
  2104.             if data.count('<body'):
  2105.                 data = data.split('<body', 1)[1]
  2106.                 if data.count('>'):
  2107.                     data = data.split('>', 1)[1]
  2108.                 
  2109.             
  2110.             if data.count('</body'):
  2111.                 data = data.split('</body', 1)[0]
  2112.             
  2113.         
  2114.     
  2115.     data = data.strip().replace('\r\n', '\n')
  2116.     return data
  2117.  
  2118.  
  2119. class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler, urllib2.HTTPDefaultErrorHandler):
  2120.     
  2121.     def http_error_default(self, req, fp, code, msg, headers):
  2122.         if code / 100 == 3 and code != 304:
  2123.             return self.http_error_302(req, fp, code, msg, headers)
  2124.         
  2125.         infourl = urllib.addinfourl(fp, headers, req.get_full_url())
  2126.         infourl.status = code
  2127.         return infourl
  2128.  
  2129.     
  2130.     def http_error_302(self, req, fp, code, msg, headers):
  2131.         if headers.dict.has_key('location'):
  2132.             infourl = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
  2133.         else:
  2134.             infourl = urllib.addinfourl(fp, headers, req.get_full_url())
  2135.         if not hasattr(infourl, 'status'):
  2136.             infourl.status = code
  2137.         
  2138.         return infourl
  2139.  
  2140.     
  2141.     def http_error_301(self, req, fp, code, msg, headers):
  2142.         if headers.dict.has_key('location'):
  2143.             infourl = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
  2144.         else:
  2145.             infourl = urllib.addinfourl(fp, headers, req.get_full_url())
  2146.         if not hasattr(infourl, 'status'):
  2147.             infourl.status = code
  2148.         
  2149.         return infourl
  2150.  
  2151.     http_error_300 = http_error_302
  2152.     http_error_303 = http_error_302
  2153.     http_error_307 = http_error_302
  2154.     
  2155.     def http_error_401(self, req, fp, code, msg, headers):
  2156.         host = urlparse.urlparse(req.get_full_url())[1]
  2157.         
  2158.         try:
  2159.             (user, passw) = base64.decodestring(req.headers['Authorization'].split(' ')[1]).split(':')
  2160.             realm = re.findall('realm="([^"]*)"', headers['WWW-Authenticate'])[0]
  2161.             self.add_password(realm, host, user, passw)
  2162.             retry = self.http_error_auth_reqed('www-authenticate', host, req, headers)
  2163.             self.reset_retry_count()
  2164.             return retry
  2165.         except:
  2166.             return self.http_error_default(req, fp, code, msg, headers)
  2167.  
  2168.  
  2169.  
  2170.  
  2171. def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers):
  2172.     if hasattr(url_file_stream_or_string, 'read'):
  2173.         return url_file_stream_or_string
  2174.     
  2175.     if url_file_stream_or_string == '-':
  2176.         return sys.stdin
  2177.     
  2178.     if urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp'):
  2179.         if not agent:
  2180.             agent = USER_AGENT
  2181.         
  2182.         auth = None
  2183.         if base64:
  2184.             (urltype, rest) = urllib.splittype(url_file_stream_or_string)
  2185.             (realhost, rest) = urllib.splithost(rest)
  2186.             if realhost:
  2187.                 (user_passwd, realhost) = urllib.splituser(realhost)
  2188.                 if user_passwd:
  2189.                     url_file_stream_or_string = '%s://%s%s' % (urltype, realhost, rest)
  2190.                     auth = base64.encodestring(user_passwd).strip()
  2191.                 
  2192.             
  2193.         
  2194.         request = urllib2.Request(url_file_stream_or_string)
  2195.         request.add_header('User-Agent', agent)
  2196.         if etag:
  2197.             request.add_header('If-None-Match', etag)
  2198.         
  2199.         if modified:
  2200.             short_weekdays = [
  2201.                 'Mon',
  2202.                 'Tue',
  2203.                 'Wed',
  2204.                 'Thu',
  2205.                 'Fri',
  2206.                 'Sat',
  2207.                 'Sun']
  2208.             months = [
  2209.                 'Jan',
  2210.                 'Feb',
  2211.                 'Mar',
  2212.                 'Apr',
  2213.                 'May',
  2214.                 'Jun',
  2215.                 'Jul',
  2216.                 'Aug',
  2217.                 'Sep',
  2218.                 'Oct',
  2219.                 'Nov',
  2220.                 'Dec']
  2221.             request.add_header('If-Modified-Since', '%s, %02d %s %04d %02d:%02d:%02d GMT' % (short_weekdays[modified[6]], modified[2], months[modified[1] - 1], modified[0], modified[3], modified[4], modified[5]))
  2222.         
  2223.         if referrer:
  2224.             request.add_header('Referer', referrer)
  2225.         
  2226.         if gzip and zlib:
  2227.             request.add_header('Accept-encoding', 'gzip, deflate')
  2228.         elif gzip:
  2229.             request.add_header('Accept-encoding', 'gzip')
  2230.         elif zlib:
  2231.             request.add_header('Accept-encoding', 'deflate')
  2232.         else:
  2233.             request.add_header('Accept-encoding', '')
  2234.         if auth:
  2235.             request.add_header('Authorization', 'Basic %s' % auth)
  2236.         
  2237.         if ACCEPT_HEADER:
  2238.             request.add_header('Accept', ACCEPT_HEADER)
  2239.         
  2240.         request.add_header('A-IM', 'feed')
  2241.         opener = apply(urllib2.build_opener, tuple([
  2242.             _FeedURLHandler()] + handlers))
  2243.         opener.addheaders = []
  2244.         
  2245.         try:
  2246.             return opener.open(request)
  2247.         finally:
  2248.             opener.close()
  2249.  
  2250.     
  2251.     
  2252.     try:
  2253.         return open(url_file_stream_or_string)
  2254.     except:
  2255.         pass
  2256.  
  2257.     return _StringIO(str(url_file_stream_or_string))
  2258.  
  2259. _date_handlers = []
  2260.  
  2261. def registerDateHandler(func):
  2262.     _date_handlers.insert(0, func)
  2263.  
  2264. _iso8601_tmpl = [
  2265.     'YYYY-?MM-?DD',
  2266.     'YYYY-MM',
  2267.     'YYYY-?OOO',
  2268.     'YY-?MM-?DD',
  2269.     'YY-?OOO',
  2270.     'YYYY',
  2271.     '-YY-?MM',
  2272.     '-OOO',
  2273.     '-YY',
  2274.     '--MM-?DD',
  2275.     '--MM',
  2276.     '---DD',
  2277.     'CC',
  2278.     '']
  2279. _iso8601_re = [ tmpl.replace('YYYY', '(?P<year>\\d{4})').replace('YY', '(?P<year>\\d\\d)').replace('MM', '(?P<month>[01]\\d)').replace('DD', '(?P<day>[0123]\\d)').replace('OOO', '(?P<ordinal>[0123]\\d\\d)').replace('CC', '(?P<century>\\d\\d$)') + '(T?(?P<hour>\\d{2}):(?P<minute>\\d{2})' + '(:(?P<second>\\d{2}))?' + '(?P<tz>[+-](?P<tzhour>\\d{2})(:(?P<tzmin>\\d{2}))?|Z)?)?' for tmpl in _iso8601_tmpl ]
  2280. del tmpl
  2281. _iso8601_matches = [ re.compile(regex).match for regex in _iso8601_re ]
  2282. del regex
  2283.  
  2284. def _parse_date_iso8601(dateString):
  2285.     m = None
  2286.     for _iso8601_match in _iso8601_matches:
  2287.         m = _iso8601_match(dateString)
  2288.         if m:
  2289.             break
  2290.             continue
  2291.     
  2292.     if not m:
  2293.         return None
  2294.     
  2295.     if m.span() == (0, 0):
  2296.         return None
  2297.     
  2298.     params = m.groupdict()
  2299.     ordinal = params.get('ordinal', 0)
  2300.     if ordinal:
  2301.         ordinal = int(ordinal)
  2302.     else:
  2303.         ordinal = 0
  2304.     year = params.get('year', '--')
  2305.     if not year or year == '--':
  2306.         year = time.gmtime()[0]
  2307.     elif len(year) == 2:
  2308.         year = 100 * int(time.gmtime()[0] / 100) + int(year)
  2309.     else:
  2310.         year = int(year)
  2311.     month = params.get('month', '-')
  2312.     if not month or month == '-':
  2313.         if ordinal:
  2314.             month = 1
  2315.         else:
  2316.             month = time.gmtime()[1]
  2317.     
  2318.     month = int(month)
  2319.     day = params.get('day', 0)
  2320.     if not day:
  2321.         if ordinal:
  2322.             day = ordinal
  2323.         elif params.get('century', 0) and params.get('year', 0) or params.get('month', 0):
  2324.             day = 1
  2325.         else:
  2326.             day = time.gmtime()[2]
  2327.     else:
  2328.         day = int(day)
  2329.     if 'century' in params.keys():
  2330.         year = (int(params['century']) - 1) * 100 + 1
  2331.     
  2332.     for field in [
  2333.         'hour',
  2334.         'minute',
  2335.         'second',
  2336.         'tzhour',
  2337.         'tzmin']:
  2338.         if not params.get(field, None):
  2339.             params[field] = 0
  2340.             continue
  2341.     
  2342.     hour = int(params.get('hour', 0))
  2343.     minute = int(params.get('minute', 0))
  2344.     second = int(params.get('second', 0))
  2345.     weekday = 0
  2346.     daylight_savings_flag = 0
  2347.     tm = [
  2348.         year,
  2349.         month,
  2350.         day,
  2351.         hour,
  2352.         minute,
  2353.         second,
  2354.         weekday,
  2355.         ordinal,
  2356.         daylight_savings_flag]
  2357.     tz = params.get('tz')
  2358.     if tz and tz != 'Z':
  2359.         if tz[0] == '-':
  2360.             tm[3] += int(params.get('tzhour', 0))
  2361.             tm[4] += int(params.get('tzmin', 0))
  2362.         elif tz[0] == '+':
  2363.             tm[3] -= int(params.get('tzhour', 0))
  2364.             tm[4] -= int(params.get('tzmin', 0))
  2365.         else:
  2366.             return None
  2367.     
  2368.     return time.localtime(time.mktime(tm))
  2369.  
  2370. registerDateHandler(_parse_date_iso8601)
  2371. _korean_year = u'\xeb\x85\x84'
  2372. _korean_month = u'\xec\x9b\x94'
  2373. _korean_day = u'\xec\x9d\xbc'
  2374. _korean_am = u'\xec\x98\xa4\xec\xa0\x84'
  2375. _korean_pm = u'\xec\x98\xa4\xed\x9b\x84'
  2376. _korean_onblog_date_re = re.compile('(\\d{4})%s\\s+(\\d{2})%s\\s+(\\d{2})%s\\s+(\\d{2}):(\\d{2}):(\\d{2})' % (_korean_year, _korean_month, _korean_day))
  2377. _korean_nate_date_re = re.compile(u'(\\d{4})-(\\d{2})-(\\d{2})\\s+(%s|%s)\\s+(\\d{,2}):(\\d{,2}):(\\d{,2})' % (_korean_am, _korean_pm))
  2378.  
  2379. def _parse_date_onblog(dateString):
  2380.     m = _korean_onblog_date_re.match(dateString)
  2381.     if not m:
  2382.         return None
  2383.     
  2384.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % {
  2385.         'year': m.group(1),
  2386.         'month': m.group(2),
  2387.         'day': m.group(3),
  2388.         'hour': m.group(4),
  2389.         'minute': m.group(5),
  2390.         'second': m.group(6),
  2391.         'zonediff': '+09:00' }
  2392.     if _debug:
  2393.         sys.stderr.write('OnBlog date parsed as: %s\n' % w3dtfdate)
  2394.     
  2395.     return _parse_date_w3dtf(w3dtfdate)
  2396.  
  2397. registerDateHandler(_parse_date_onblog)
  2398.  
  2399. def _parse_date_nate(dateString):
  2400.     m = _korean_nate_date_re.match(dateString)
  2401.     if not m:
  2402.         return None
  2403.     
  2404.     hour = int(m.group(5))
  2405.     ampm = m.group(4)
  2406.     if ampm == _korean_pm:
  2407.         hour += 12
  2408.     
  2409.     hour = str(hour)
  2410.     if len(hour) == 1:
  2411.         hour = '0' + hour
  2412.     
  2413.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % {
  2414.         'year': m.group(1),
  2415.         'month': m.group(2),
  2416.         'day': m.group(3),
  2417.         'hour': hour,
  2418.         'minute': m.group(6),
  2419.         'second': m.group(7),
  2420.         'zonediff': '+09:00' }
  2421.     if _debug:
  2422.         sys.stderr.write('Nate date parsed as: %s\n' % w3dtfdate)
  2423.     
  2424.     return _parse_date_w3dtf(w3dtfdate)
  2425.  
  2426. registerDateHandler(_parse_date_nate)
  2427. _mssql_date_re = re.compile('(\\d{4})-(\\d{2})-(\\d{2})\\s+(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?')
  2428.  
  2429. def _parse_date_mssql(dateString):
  2430.     m = _mssql_date_re.match(dateString)
  2431.     if not m:
  2432.         return None
  2433.     
  2434.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % {
  2435.         'year': m.group(1),
  2436.         'month': m.group(2),
  2437.         'day': m.group(3),
  2438.         'hour': m.group(4),
  2439.         'minute': m.group(5),
  2440.         'second': m.group(6),
  2441.         'zonediff': '+09:00' }
  2442.     if _debug:
  2443.         sys.stderr.write('MS SQL date parsed as: %s\n' % w3dtfdate)
  2444.     
  2445.     return _parse_date_w3dtf(w3dtfdate)
  2446.  
  2447. registerDateHandler(_parse_date_mssql)
  2448. _greek_months = {
  2449.     u'\xce\x99\xce\xb1\xce\xbd': u'Jan',
  2450.     u'\xce\xa6\xce\xb5\xce\xb2': u'Feb',
  2451.     u'\xce\x9c\xce\xac\xcf\x8e': u'Mar',
  2452.     u'\xce\x9c\xce\xb1\xcf\x8e': u'Mar',
  2453.     u'\xce\x91\xcf\x80\xcf\x81': u'Apr',
  2454.     u'\xce\x9c\xce\xac\xce\xb9': u'May',
  2455.     u'\xce\x9c\xce\xb1\xcf\x8a': u'May',
  2456.     u'\xce\x9c\xce\xb1\xce\xb9': u'May',
  2457.     u'\xce\x99\xce\xbf\xcf\x8d\xce\xbd': u'Jun',
  2458.     u'\xce\x99\xce\xbf\xce\xbd': u'Jun',
  2459.     u'\xce\x99\xce\xbf\xcf\x8d\xce\xbb': u'Jul',
  2460.     u'\xce\x99\xce\xbf\xce\xbb': u'Jul',
  2461.     u'\xce\x91\xcf\x8d\xce\xb3': u'Aug',
  2462.     u'\xce\x91\xcf\x85\xce\xb3': u'Aug',
  2463.     u'\xce\xa3\xce\xb5\xcf\x80': u'Sep',
  2464.     u'\xce\x9f\xce\xba\xcf\x84': u'Oct',
  2465.     u'\xce\x9d\xce\xbf\xce\xad': u'Nov',
  2466.     u'\xce\x9d\xce\xbf\xce\xb5': u'Nov',
  2467.     u'\xce\x94\xce\xb5\xce\xba': u'Dec' }
  2468. _greek_wdays = {
  2469.     u'\xce\x9a\xcf\x85\xcf\x81': u'Sun',
  2470.     u'\xce\x94\xce\xb5\xcf\x85': u'Mon',
  2471.     u'\xce\xa4\xcf\x81\xce\xb9': u'Tue',
  2472.     u'\xce\xa4\xce\xb5\xcf\x84': u'Wed',
  2473.     u'\xce\xa0\xce\xb5\xce\xbc': u'Thu',
  2474.     u'\xce\xa0\xce\xb1\xcf\x81': u'Fri',
  2475.     u'\xce\xa3\xce\xb1\xce\xb2': u'Sat' }
  2476. _greek_date_format_re = re.compile(u'([^,]+),\\s+(\\d{2})\\s+([^\\s]+)\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+([^\\s]+)')
  2477.  
  2478. def _parse_date_greek(dateString):
  2479.     m = _greek_date_format_re.match(dateString)
  2480.     if not m:
  2481.         return None
  2482.     
  2483.     
  2484.     try:
  2485.         wday = _greek_wdays[m.group(1)]
  2486.         month = _greek_months[m.group(3)]
  2487.     except:
  2488.         return None
  2489.  
  2490.     rfc822date = '%(wday)s, %(day)s %(month)s %(year)s %(hour)s:%(minute)s:%(second)s %(zonediff)s' % {
  2491.         'wday': wday,
  2492.         'day': m.group(2),
  2493.         'month': month,
  2494.         'year': m.group(4),
  2495.         'hour': m.group(5),
  2496.         'minute': m.group(6),
  2497.         'second': m.group(7),
  2498.         'zonediff': m.group(8) }
  2499.     if _debug:
  2500.         sys.stderr.write('Greek date parsed as: %s\n' % rfc822date)
  2501.     
  2502.     return _parse_date_rfc822(rfc822date)
  2503.  
  2504. registerDateHandler(_parse_date_greek)
  2505. _hungarian_months = {
  2506.     u'janu\xc3\xa1r': u'01',
  2507.     u'febru\xc3\xa1ri': u'02',
  2508.     u'm\xc3\xa1rcius': u'03',
  2509.     u'\xc3\xa1prilis': u'04',
  2510.     u'm\xc3\xa1ujus': u'05',
  2511.     u'j\xc3\xbanius': u'06',
  2512.     u'j\xc3\xbalius': u'07',
  2513.     u'augusztus': u'08',
  2514.     u'szeptember': u'09',
  2515.     u'okt\xc3\xb3ber': u'10',
  2516.     u'november': u'11',
  2517.     u'december': u'12' }
  2518. _hungarian_date_format_re = re.compile(u'(\\d{4})-([^-]+)-(\\d{,2})T(\\d{,2}):(\\d{2})((\\+|-)(\\d{,2}:\\d{2}))')
  2519.  
  2520. def _parse_date_hungarian(dateString):
  2521.     m = _hungarian_date_format_re.match(dateString)
  2522.     if not m:
  2523.         return None
  2524.     
  2525.     
  2526.     try:
  2527.         month = _hungarian_months[m.group(2)]
  2528.         day = m.group(3)
  2529.         if len(day) == 1:
  2530.             day = '0' + day
  2531.         
  2532.         hour = m.group(4)
  2533.         if len(hour) == 1:
  2534.             hour = '0' + hour
  2535.     except:
  2536.         return None
  2537.  
  2538.     w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s%(zonediff)s' % {
  2539.         'year': m.group(1),
  2540.         'month': month,
  2541.         'day': day,
  2542.         'hour': hour,
  2543.         'minute': m.group(5),
  2544.         'zonediff': m.group(6) }
  2545.     if _debug:
  2546.         sys.stderr.write('Hungarian date parsed as: %s\n' % w3dtfdate)
  2547.     
  2548.     return _parse_date_w3dtf(w3dtfdate)
  2549.  
  2550. registerDateHandler(_parse_date_hungarian)
  2551.  
  2552. def _parse_date_w3dtf(dateString):
  2553.     
  2554.     def __extract_date(m):
  2555.         year = int(m.group('year'))
  2556.         if year < 100:
  2557.             year = 100 * int(time.gmtime()[0] / 100) + int(year)
  2558.         
  2559.         if year < 1000:
  2560.             return (0, 0, 0)
  2561.         
  2562.         julian = m.group('julian')
  2563.         if julian:
  2564.             julian = int(julian)
  2565.             month = julian / 30 + 1
  2566.             day = julian % 30 + 1
  2567.             jday = None
  2568.             while jday != julian:
  2569.                 t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0))
  2570.                 jday = time.gmtime(t)[-2]
  2571.                 diff = abs(jday - julian)
  2572.                 if jday > julian:
  2573.                     if diff < day:
  2574.                         day = day - diff
  2575.                     else:
  2576.                         month = month - 1
  2577.                         day = 31
  2578.                 diff < day
  2579.                 if jday < julian:
  2580.                     if day + diff < 28:
  2581.                         day = day + diff
  2582.                     else:
  2583.                         month = month + 1
  2584.                 day + diff < 28
  2585.             return (year, month, day)
  2586.         
  2587.         month = m.group('month')
  2588.         day = 1
  2589.         if month is None:
  2590.             month = 1
  2591.         else:
  2592.             month = int(month)
  2593.             day = m.group('day')
  2594.             if day:
  2595.                 day = int(day)
  2596.             else:
  2597.                 day = 1
  2598.         return (year, month, day)
  2599.  
  2600.     
  2601.     def __extract_time(m):
  2602.         if not m:
  2603.             return (0, 0, 0)
  2604.         
  2605.         hours = m.group('hours')
  2606.         if not hours:
  2607.             return (0, 0, 0)
  2608.         
  2609.         hours = int(hours)
  2610.         minutes = int(m.group('minutes'))
  2611.         seconds = m.group('seconds')
  2612.         if seconds:
  2613.             seconds = int(seconds)
  2614.         else:
  2615.             seconds = 0
  2616.         return (hours, minutes, seconds)
  2617.  
  2618.     
  2619.     def __extract_tzd(m):
  2620.         if not m:
  2621.             return 0
  2622.         
  2623.         tzd = m.group('tzd')
  2624.         if not tzd:
  2625.             return 0
  2626.         
  2627.         if tzd == 'Z':
  2628.             return 0
  2629.         
  2630.         hours = int(m.group('tzdhours'))
  2631.         minutes = m.group('tzdminutes')
  2632.         if minutes:
  2633.             minutes = int(minutes)
  2634.         else:
  2635.             minutes = 0
  2636.         offset = (hours * 60 + minutes) * 60
  2637.         if tzd[0] == '+':
  2638.             return -offset
  2639.         
  2640.         return offset
  2641.  
  2642.     __date_re = '(?P<year>\\d\\d\\d\\d)(?:(?P<dsep>-|)(?:(?P<julian>\\d\\d\\d)|(?P<month>\\d\\d)(?:(?P=dsep)(?P<day>\\d\\d))?))?'
  2643.     __tzd_re = '(?P<tzd>[-+](?P<tzdhours>\\d\\d)(?::?(?P<tzdminutes>\\d\\d))|Z)'
  2644.     __tzd_rx = re.compile(__tzd_re)
  2645.     __time_re = '(?P<hours>\\d\\d)(?P<tsep>:|)(?P<minutes>\\d\\d)(?:(?P=tsep)(?P<seconds>\\d\\d(?:[.,]\\d+)?))?' + __tzd_re
  2646.     __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re)
  2647.     __datetime_rx = re.compile(__datetime_re)
  2648.     m = __datetime_rx.match(dateString)
  2649.     if m is None or m.group() != dateString:
  2650.         return None
  2651.     
  2652.     gmt = __extract_date(m) + __extract_time(m) + (0, 0, 0)
  2653.     if gmt[0] == 0:
  2654.         return None
  2655.     
  2656.     return time.gmtime(time.mktime(gmt) + __extract_tzd(m) - time.timezone)
  2657.  
  2658. registerDateHandler(_parse_date_w3dtf)
  2659.  
  2660. def _parse_date_rfc822(dateString):
  2661.     data = dateString.split()
  2662.     if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames:
  2663.         del data[0]
  2664.     
  2665.     if len(data) == 4:
  2666.         s = data[3]
  2667.         i = s.find('+')
  2668.         if i > 0:
  2669.             data[3:] = [
  2670.                 s[:i],
  2671.                 s[i + 1:]]
  2672.         else:
  2673.             data.append('')
  2674.         dateString = ' '.join(data)
  2675.     
  2676.     if len(data) < 5:
  2677.         dateString += ' 00:00:00 GMT'
  2678.     
  2679.     tm = rfc822.parsedate_tz(dateString)
  2680.     if tm:
  2681.         return time.gmtime(rfc822.mktime_tz(tm))
  2682.     
  2683.  
  2684. _additional_timezones = {
  2685.     'AT': -400,
  2686.     'ET': -500,
  2687.     'CT': -600,
  2688.     'MT': -700,
  2689.     'PT': -800 }
  2690. rfc822._timezones.update(_additional_timezones)
  2691. registerDateHandler(_parse_date_rfc822)
  2692.  
  2693. def _parse_date(dateString):
  2694.     for handler in _date_handlers:
  2695.         
  2696.         try:
  2697.             date9tuple = handler(dateString)
  2698.             if not date9tuple:
  2699.                 continue
  2700.             
  2701.             if len(date9tuple) != 9:
  2702.                 if _debug:
  2703.                     sys.stderr.write('date handler function must return 9-tuple\n')
  2704.                 
  2705.                 raise ValueError
  2706.             
  2707.             map(int, date9tuple)
  2708.             return date9tuple
  2709.         continue
  2710.         except Exception:
  2711.             e = None
  2712.             if _debug:
  2713.                 sys.stderr.write('%s raised %s\n' % (handler.__name__, repr(e)))
  2714.             
  2715.             _debug
  2716.         
  2717.  
  2718.     
  2719.  
  2720.  
  2721. def _getCharacterEncoding(http_headers, xml_data):
  2722.     
  2723.     def _parseHTTPContentType(content_type):
  2724.         if not content_type:
  2725.             pass
  2726.         content_type = ''
  2727.         (content_type, params) = cgi.parse_header(content_type)
  2728.         return (content_type, params.get('charset', '').replace("'", ''))
  2729.  
  2730.     sniffed_xml_encoding = ''
  2731.     xml_encoding = ''
  2732.     true_encoding = ''
  2733.     (http_content_type, http_encoding) = _parseHTTPContentType(http_headers.get('content-type'))
  2734.     
  2735.     try:
  2736.         if xml_data[:4] == 'Lo\xa7\x94':
  2737.             xml_data = _ebcdic_to_ascii(xml_data)
  2738.         elif xml_data[:4] == '\x00<\x00?':
  2739.             sniffed_xml_encoding = 'utf-16be'
  2740.             xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
  2741.         elif len(xml_data) >= 4 and xml_data[:2] == '\xfe\xff' and xml_data[2:4] != '\x00\x00':
  2742.             sniffed_xml_encoding = 'utf-16be'
  2743.             xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
  2744.         elif xml_data[:4] == '<\x00?\x00':
  2745.             sniffed_xml_encoding = 'utf-16le'
  2746.             xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
  2747.         elif len(xml_data) >= 4 and xml_data[:2] == '\xff\xfe' and xml_data[2:4] != '\x00\x00':
  2748.             sniffed_xml_encoding = 'utf-16le'
  2749.             xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
  2750.         elif xml_data[:4] == '\x00\x00\x00<':
  2751.             sniffed_xml_encoding = 'utf-32be'
  2752.             xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
  2753.         elif xml_data[:4] == '<\x00\x00\x00':
  2754.             sniffed_xml_encoding = 'utf-32le'
  2755.             xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
  2756.         elif xml_data[:4] == '\x00\x00\xfe\xff':
  2757.             sniffed_xml_encoding = 'utf-32be'
  2758.             xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
  2759.         elif xml_data[:4] == '\xff\xfe\x00\x00':
  2760.             sniffed_xml_encoding = 'utf-32le'
  2761.             xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
  2762.         elif xml_data[:3] == '\xef\xbb\xbf':
  2763.             sniffed_xml_encoding = 'utf-8'
  2764.             xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
  2765.         
  2766.         xml_encoding_match = re.compile('^<\\?.*encoding=[\'"](.*?)[\'"].*\\?>').match(xml_data)
  2767.     except:
  2768.         xml_encoding_match = None
  2769.  
  2770.     if xml_encoding_match:
  2771.         xml_encoding = xml_encoding_match.groups()[0].lower()
  2772.         if sniffed_xml_encoding and xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16'):
  2773.             xml_encoding = sniffed_xml_encoding
  2774.         
  2775.     
  2776.     acceptable_content_type = 0
  2777.     application_content_types = ('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity')
  2778.     text_content_types = ('text/xml', 'text/xml-external-parsed-entity')
  2779.     if (http_content_type in application_content_types or http_content_type.startswith('application/')) and http_content_type.endswith('+xml'):
  2780.         acceptable_content_type = 1
  2781.         if not http_encoding and xml_encoding:
  2782.             pass
  2783.         true_encoding = 'utf-8'
  2784.     elif (http_content_type in text_content_types or http_content_type.startswith('text/')) and http_content_type.endswith('+xml'):
  2785.         acceptable_content_type = 1
  2786.         if not http_encoding:
  2787.             pass
  2788.         true_encoding = 'us-ascii'
  2789.     elif http_content_type.startswith('text/'):
  2790.         if not http_encoding:
  2791.             pass
  2792.         true_encoding = 'us-ascii'
  2793.     elif http_headers and not http_headers.has_key('content-type'):
  2794.         if not xml_encoding:
  2795.             pass
  2796.         true_encoding = 'iso-8859-1'
  2797.     elif not xml_encoding:
  2798.         pass
  2799.     true_encoding = 'utf-8'
  2800.     return (true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type)
  2801.  
  2802.  
  2803. def _toUTF8(data, encoding):
  2804.     if _debug:
  2805.         sys.stderr.write('entering _toUTF8, trying encoding %s\n' % encoding)
  2806.     
  2807.     if len(data) >= 4 and data[:2] == '\xfe\xff' and data[2:4] != '\x00\x00':
  2808.         if _debug:
  2809.             sys.stderr.write('stripping BOM\n')
  2810.             if encoding != 'utf-16be':
  2811.                 sys.stderr.write('trying utf-16be instead\n')
  2812.             
  2813.         
  2814.         encoding = 'utf-16be'
  2815.         data = data[2:]
  2816.     elif len(data) >= 4 and data[:2] == '\xff\xfe' and data[2:4] != '\x00\x00':
  2817.         if _debug:
  2818.             sys.stderr.write('stripping BOM\n')
  2819.             if encoding != 'utf-16le':
  2820.                 sys.stderr.write('trying utf-16le instead\n')
  2821.             
  2822.         
  2823.         encoding = 'utf-16le'
  2824.         data = data[2:]
  2825.     elif data[:3] == '\xef\xbb\xbf':
  2826.         if _debug:
  2827.             sys.stderr.write('stripping BOM\n')
  2828.             if encoding != 'utf-8':
  2829.                 sys.stderr.write('trying utf-8 instead\n')
  2830.             
  2831.         
  2832.         encoding = 'utf-8'
  2833.         data = data[3:]
  2834.     elif data[:4] == '\x00\x00\xfe\xff':
  2835.         if _debug:
  2836.             sys.stderr.write('stripping BOM\n')
  2837.             if encoding != 'utf-32be':
  2838.                 sys.stderr.write('trying utf-32be instead\n')
  2839.             
  2840.         
  2841.         encoding = 'utf-32be'
  2842.         data = data[4:]
  2843.     elif data[:4] == '\xff\xfe\x00\x00':
  2844.         if _debug:
  2845.             sys.stderr.write('stripping BOM\n')
  2846.             if encoding != 'utf-32le':
  2847.                 sys.stderr.write('trying utf-32le instead\n')
  2848.             
  2849.         
  2850.         encoding = 'utf-32le'
  2851.         data = data[4:]
  2852.     
  2853.     newdata = unicode(data, encoding)
  2854.     if _debug:
  2855.         sys.stderr.write('successfully converted %s data to unicode\n' % encoding)
  2856.     
  2857.     declmatch = re.compile('^<\\?xml[^>]*?>')
  2858.     newdecl = "<?xml version='1.0' encoding='utf-8'?>"
  2859.     if declmatch.search(newdata):
  2860.         newdata = declmatch.sub(newdecl, newdata)
  2861.     else:
  2862.         newdata = newdecl + u'\n' + newdata
  2863.     return newdata.encode('utf-8')
  2864.  
  2865.  
  2866. def _stripDoctype(data):
  2867.     entity_pattern = re.compile('<!ENTITY([^>]*?)>', re.MULTILINE)
  2868.     data = entity_pattern.sub('', data)
  2869.     doctype_pattern = re.compile('<!DOCTYPE([^>]*?)>', re.MULTILINE)
  2870.     doctype_results = doctype_pattern.findall(data)
  2871.     if not doctype_results or doctype_results[0]:
  2872.         pass
  2873.     doctype = ''
  2874.     if doctype.lower().count('netscape'):
  2875.         version = 'rss091n'
  2876.     else:
  2877.         version = None
  2878.     data = doctype_pattern.sub('', data)
  2879.     return (version, data)
  2880.  
  2881.  
  2882. def parse(url_file_stream_or_string, etag = None, modified = None, agent = None, referrer = None, handlers = []):
  2883.     result = FeedParserDict()
  2884.     result['feed'] = FeedParserDict()
  2885.     result['entries'] = []
  2886.     if _XML_AVAILABLE:
  2887.         result['bozo'] = 0
  2888.     
  2889.     if type(handlers) == types.InstanceType:
  2890.         handlers = [
  2891.             handlers]
  2892.     
  2893.     
  2894.     try:
  2895.         f = _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers)
  2896.         data = f.read()
  2897.     except Exception:
  2898.         e = None
  2899.         result['bozo'] = 1
  2900.         result['bozo_exception'] = e
  2901.         data = ''
  2902.         f = None
  2903.  
  2904.     if f and data and hasattr(f, 'headers'):
  2905.         if gzip and f.headers.get('content-encoding', '') == 'gzip':
  2906.             
  2907.             try:
  2908.                 data = gzip.GzipFile(fileobj = _StringIO(data)).read()
  2909.             except Exception:
  2910.                 e = None
  2911.                 result['bozo'] = 1
  2912.                 result['bozo_exception'] = e
  2913.                 data = ''
  2914.             except:
  2915.                 None<EXCEPTION MATCH>Exception
  2916.             
  2917.  
  2918.         None<EXCEPTION MATCH>Exception
  2919.         if zlib and f.headers.get('content-encoding', '') == 'deflate':
  2920.             
  2921.             try:
  2922.                 data = zlib.decompress(data, -(zlib.MAX_WBITS))
  2923.             except Exception:
  2924.                 e = None
  2925.                 result['bozo'] = 1
  2926.                 result['bozo_exception'] = e
  2927.                 data = ''
  2928.             except:
  2929.                 None<EXCEPTION MATCH>Exception
  2930.             
  2931.  
  2932.         None<EXCEPTION MATCH>Exception
  2933.     
  2934.     if hasattr(f, 'info'):
  2935.         info = f.info()
  2936.         result['etag'] = info.getheader('ETag')
  2937.         last_modified = info.getheader('Last-Modified')
  2938.         if last_modified:
  2939.             result['modified'] = _parse_date(last_modified)
  2940.         
  2941.     
  2942.     if hasattr(f, 'url'):
  2943.         result['href'] = f.url
  2944.         result['status'] = 200
  2945.     
  2946.     if hasattr(f, 'status'):
  2947.         result['status'] = f.status
  2948.     
  2949.     if hasattr(f, 'headers'):
  2950.         result['headers'] = f.headers.dict
  2951.     
  2952.     if hasattr(f, 'close'):
  2953.         f.close()
  2954.     
  2955.     http_headers = result.get('headers', { })
  2956.     (result['encoding'], http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type) = _getCharacterEncoding(http_headers, data)
  2957.     if http_headers and not acceptable_content_type:
  2958.         if http_headers.has_key('content-type'):
  2959.             bozo_message = '%s is not an XML media type' % http_headers['content-type']
  2960.         else:
  2961.             bozo_message = 'no Content-type specified'
  2962.         result['bozo'] = 1
  2963.         result['bozo_exception'] = NonXMLContentType(bozo_message)
  2964.     
  2965.     (result['version'], data) = _stripDoctype(data)
  2966.     baseuri = http_headers.get('content-location', result.get('href'))
  2967.     baselang = http_headers.get('content-language', None)
  2968.     if result.get('status', 0) == 304:
  2969.         result['version'] = ''
  2970.         result['debug_message'] = 'The feed has not changed since you last checked, ' + 'so the server sent no data.  This is a feature, not a bug!'
  2971.         return result
  2972.     
  2973.     if not data:
  2974.         return result
  2975.     
  2976.     use_strict_parser = 0
  2977.     known_encoding = 0
  2978.     tried_encodings = []
  2979.     for proposed_encoding in (result['encoding'], xml_encoding, sniffed_xml_encoding):
  2980.         if not proposed_encoding:
  2981.             continue
  2982.         
  2983.         if proposed_encoding in tried_encodings:
  2984.             continue
  2985.         
  2986.         tried_encodings.append(proposed_encoding)
  2987.         
  2988.         try:
  2989.             data = _toUTF8(data, proposed_encoding)
  2990.             known_encoding = use_strict_parser = 1
  2991.         continue
  2992.         continue
  2993.  
  2994.     
  2995.     if not known_encoding and chardet:
  2996.         
  2997.         try:
  2998.             proposed_encoding = chardet.detect(data)['encoding']
  2999.             if proposed_encoding and proposed_encoding not in tried_encodings:
  3000.                 tried_encodings.append(proposed_encoding)
  3001.                 data = _toUTF8(data, proposed_encoding)
  3002.                 known_encoding = use_strict_parser = 1
  3003.  
  3004.     
  3005.     if not known_encoding and 'utf-8' not in tried_encodings:
  3006.         
  3007.         try:
  3008.             proposed_encoding = 'utf-8'
  3009.             tried_encodings.append(proposed_encoding)
  3010.             data = _toUTF8(data, proposed_encoding)
  3011.             known_encoding = use_strict_parser = 1
  3012.  
  3013.     
  3014.     if not known_encoding and 'windows-1252' not in tried_encodings:
  3015.         
  3016.         try:
  3017.             proposed_encoding = 'windows-1252'
  3018.             tried_encodings.append(proposed_encoding)
  3019.             data = _toUTF8(data, proposed_encoding)
  3020.             known_encoding = use_strict_parser = 1
  3021.  
  3022.     
  3023.     if not known_encoding:
  3024.         result['bozo'] = 1
  3025.         result['bozo_exception'] = CharacterEncodingUnknown('document encoding unknown, I tried ' + '%s, %s, utf-8, and windows-1252 but nothing worked' % (result['encoding'], xml_encoding))
  3026.         result['encoding'] = ''
  3027.     elif proposed_encoding != result['encoding']:
  3028.         result['bozo'] = 1
  3029.         result['bozo_exception'] = CharacterEncodingOverride('documented declared as %s, but parsed as %s' % (result['encoding'], proposed_encoding))
  3030.         result['encoding'] = proposed_encoding
  3031.     
  3032.     if not _XML_AVAILABLE:
  3033.         use_strict_parser = 0
  3034.     
  3035.     if use_strict_parser:
  3036.         feedparser = _StrictFeedParser(baseuri, baselang, 'utf-8')
  3037.         saxparser = xml.sax.make_parser(PREFERRED_XML_PARSERS)
  3038.         saxparser.setFeature(xml.sax.handler.feature_namespaces, 1)
  3039.         saxparser.setContentHandler(feedparser)
  3040.         saxparser.setErrorHandler(feedparser)
  3041.         source = xml.sax.xmlreader.InputSource()
  3042.         source.setByteStream(_StringIO(data))
  3043.         if hasattr(saxparser, '_ns_stack'):
  3044.             saxparser._ns_stack.append({
  3045.                 'http://www.w3.org/XML/1998/namespace': 'xml' })
  3046.         
  3047.         
  3048.         try:
  3049.             saxparser.parse(source)
  3050.         except Exception:
  3051.             e = None
  3052.             if _debug:
  3053.                 import traceback as traceback
  3054.                 traceback.print_stack()
  3055.                 traceback.print_exc()
  3056.                 sys.stderr.write('xml parsing failed\n')
  3057.             
  3058.             result['bozo'] = 1
  3059.             if not feedparser.exc:
  3060.                 pass
  3061.             result['bozo_exception'] = e
  3062.             use_strict_parser = 0
  3063.         except:
  3064.             None<EXCEPTION MATCH>Exception
  3065.         
  3066.  
  3067.     None<EXCEPTION MATCH>Exception
  3068.     if not use_strict_parser:
  3069.         if not known_encoding or 'utf-8':
  3070.             pass
  3071.         feedparser = _LooseFeedParser(baseuri, baselang, '')
  3072.         feedparser.feed(data)
  3073.     
  3074.     result['feed'] = feedparser.feeddata
  3075.     result['entries'] = feedparser.entries
  3076.     if not result['version']:
  3077.         pass
  3078.     result['version'] = feedparser.version
  3079.     result['namespaces'] = feedparser.namespacesInUse
  3080.     return result
  3081.  
  3082. if __name__ == '__main__':
  3083.     zopeCompatibilityHack()
  3084.     from pprint import pprint
  3085.     for url in urls:
  3086.         print url
  3087.         print 
  3088.         result = parse(url)
  3089.         pprint(result)
  3090.         print 
  3091.     
  3092.  
  3093.